Vượt qua được các cuộc phỏng vấn JavaScript (JS) không hề dễ dàng. Số lượng các câu hỏi có thể được hỏi trong một cuộc phỏng vấn JavaScript là khá nhiều và đa dạng. Làm thế nào để bạn  có thể thành công trong một cuộc phỏng vấn JavaScript? Bắt đầu từ đâu? Bài viết này là một nỗ lực để hướng dẫn tất cả các JavaScript developers có mong muốn củng cố kiến thức JavaScript của họ với các khái niệm cơ bản. Đây là những bước nhỏ cần thực hiện đầu tiên để đối mặt với một cuộc phỏng vấn JavaScript. Nếu bạn là một ứng cử viên, bạn cần chuẩn bị tốt cho những khái niệm này. Nếu bạn là một người phỏng vấn, bạn cần bắt đầu với những điều căn bản này trước khi đến với những câu hỏi sâu hơn.

Hướng dẫn này là một điểm bắt đầu nhưng không phải là tất cả cho một JavaScript developer. Bạn cần chuẩn bị cho những cuộc phỏng vấn khó khăn hơn nhiều. Nhiều người nói rằng thực sự khó để tìm được một nhà phát triển JavaScript giỏi!

JavaScript rất khó. Nhưng chúng ta có thể làm gì? Nắm vững mười một yếu tố cơ bản dưới đây để biến một cuộc phỏng vấn JavaScript trở nên thuận lợi với bạn.

1) Hiểu rõ các hàm trong JavaScript

Các hàm là nền tảng của JavaScript. Nếu không biết các hàm JavaScript chuyên sâu, kiến thức của bạn sẽ bị hạn chế nghiêm trọng. Một hàm JavaScript không chỉ là một hàm bình thường. Không giống như các ngôn ngữ khác, một hàm có thể được gán cho một biến, được truyền xung quanh như một đối số cho một hàm khác và cũng có thể được trả về từ một hàm khác. Nói một cách ví von, hàm là công dân hạng nhất trong JS.

Chúng ta sẽ không giải thích hàm là gì ở đây. Điều quan trọng là bạn cần phải nắm vững về các hàm trong JV và tài liệu này là một tài liệu bạn nên đọc.

2) Hiểu rõ bind(), apply() và call()

Những hàm này bạn có thể thấy trong tất cả các thư viện của JavaScript. Chúng cho phép bạn sử dụng một kỹ thuật gọi là currying để chuyển đổi mt hàm vi nhiu tham s thành những hàm liên tiếp có mt tham số. Hay nói cách khác, thay vì truyền vào cho hàm một lúc nhiều tham số, chúng ta lại chuyển kiểu viết đó thành 1 hàm chỉ nhận 1 tham số, nhưng bên trong đó chúng ta lồng các hàm con bên trong, và trả về hàm con này  Một nhà phát triển JavaScript giỏi có thể cho bạn biết về ba điều hàm này bất cứ lúc nào.

Về cơ bản, đây là các phương pháp nguyên mẫu (prototype methods) của các hàm để thay đổi hành vi nhằm đạt được điều gì đó. Về cơ bản, cách sử dụng các hàm như sau:

  • Sử dụng .bind () khi bạn muốn hàm đó sau này được gọi với một ngữ cảnh (context) nhất định, hữu ích trong các sự kiện (event).
  • Sử dụng .call () hoặc .apply () khi bạn muốn gọi hàm ngay lập tức, có sửa đổi ngữ cảnh.

Bạn có thể tìm hiểu chi tiết về ba hàm này theo các link sau: Hàm bind, hàm callhàm apply

Bạn cũng có thể tìm hiểu thêm về sự khác nhau của 3 hàm này trên Kiapalog hoặc tại đây

3) Hiểu JavaScript scope và Closures

JavaCript scope

JavaScript scope là một chiếc hộp pandora. Hàng trăm câu hỏi phỏng vấn khó có thể được đặt ra xoay quanh khái niệm này. Hiểu một cách đơn giản nhất, scope trong JavaScript có nghĩa là “nơi một phần tử (element), dữ liệu (data)  hoặc giá trị (value) có thể sử dụng được” trong script.  Có ba loại scope:

Chúng ta hãy cùng tìm hiểu nhanh về 3 loại scope này

1/Global scope

Hãy xem ví dụ sau:

<html>
<body>
<script language="javascript">

// Biến toàn cục
var comment = "đây là biến global";

// Hàm có sử dụng biến global
function add_comment()
{
alert(comment);
}

// In biến toàn cục
alert(comment);
</script>
</body>
</html>

Trong đoạn script trên, khai báo cpmment có giá trị khởi tạo là "đây là biến global" có sẵn cho phạm vi toàn cục (global) và có thể truy cập ở bất kỳ đâu trong script. Như vậy global scope là scope mà bất cứ biến và hàm nào được định nghĩa trong nó đều có thể truy cập được ở bất kì nơi nào.

2/Local hay function scope

Trong ví dụ dưới đây, trong hàm add_comment() , comment được khai báo lại và khởi tạo trong khi gán cho nó giá trị là "đây là biến local". Các biến cục bộ như var comment = "đây là biến local" bên trong hàm  được tạo khi hàm bắt đầu và bị xóa khi hàm đang chạy. 

Comment độc lập với global scope và chỉ có thể truy cập trong hàm add_comment(). Lưu ý, khi ở bên trong một hàm, nếu ta khai báo biến mà không dùng từ khóa var thì biến đó sẽ trở thành biến toàn cục.

<html>
<body>
<script language="javascript">

// Biến global
var comment = "đây là biến global";

// Hàm có sử dụng biến global
function add_comment()
{
var comment = "đây là biến local";
alert(comment);
}

// Gọi hàm comment
add_comment();

// In biến global
alert(comment);

</script>
</body>
</html>

3/Block scope

ES16 đã giới thiệu một block scope giới hạn phạm vi của một biến trong một khối dấu ngoặc nhất định.

var a = 10;

function Foo() {
if (true) {
let a = 4;
}

alert(a); // alerts '10' because the 'let' keyword
}Foo();

Các hàm & điều kiện được coi là các khối. Ví dụ trên sẽ alert 4 vì các câu lệnh điều kiện được thực thi. Nhưng ES6 phá hủy scope của các biến khối và scope đã trở thành global.

Closures

Bây giờ đến phần ma thuật của scope. Nó có thể đạt được bằng cách sử dụng các closure. JavaScript closure là một hàm trả về một hàm khác.

Nếu ai đó hỏi bạn câu hỏi này. Viết một thiết kế lấy một chuỗi và trả về một ký tự tại một thời điểm. Nếu chuỗi mới được đưa ra, nó sẽ thay thế chuỗi cũ. Nó được gọi đơn giản là generator.

function generator(input) {
var index = 0;
return {
next: function() {
if (index < input.length) {
index += 1;
return input[index - 1];
}
return "";
}
}
}

Cách thực thi

var mygenerator = generator("boomerang");
mygenerator.next(); // returns "b"
mygenerator.next() // returns "o"mygenerator = generator("toon");
mygenerator.next(); // returns "t"

Ở đây, scope đang đóng một vai trò quan trọng. Closure là một hàm trả về một hàm khác và kết thúc dữ liệu. Trình tạo chuỗi trên đủ điều kiện cho một closure. Giá trị chỉ mục (index value) được giữ nguyên giữa nhiều lần gọi hàm. Hàm bên trong được định nghĩa có thể truy cập các biến được xác định trong hàm mẹ. Đây là một scope khác. Nếu bạn đã xác định thêm một hàm trong hàm cấp hai, hàm đó có thể truy cập vào tất cả các biến của parents.

4) Hiểu về các keyword global, function và object scopes

Trong JavaScript, chúng ta luôn hàm và đối tượng khi viết code. Nếu bạn sử dụng trình duyệt, trong ngữ cảnh chung, nó đề cập đến đối tượng cửa sổ. Bây giờ bạn hãy thử:

this === window;

Khi bối cảnh (conetxt) và phạm vi (scope) của chương trình thay đổi, this tại thời điểm cụ thể đó cũng thay đổi theo. Bây giờ hãy xem this trong bối cảnh cục bộ:

function Foo(){
console.log(this.a);
}var food = {a: "Magical this"};Foo.call(food); // food is this

Bạn hãy dự đoán đầu ra xem?

function Foo(){
console.log(this); // prints {}?
}

Không, sẽ không. Bởi vì đây là một đối tượng toàn cục (global object). Hãy nhớ rằng, bất kể parent scope là gì, nó sẽ được kế thừa bởi con. Vì vậy, nó in đối tượng cửa sổ (window object). Ba phương pháp chúng ta đã thảo luận thực sự được sử dụng để thiết lập this object.

Bây giờ đến this trong object scope:

var person = {
name: "Stranger",
age: 24,
get identity() {
return {who: this.name, howOld: this.age};
}
}

Chúng ta sử dụng cú pháp getter là một hàm có thể được gọi như một biến.

person.identity; // returns {who: "Stranger", howOld: 24}

Ở đây, điều này thực sự đang đề cập đến chính đối tượng. This như chúng ta đã đề cập trước đây hoạt động khác nhau ở những nơi khác nhau. Hãy chắc chắn bạn nắm rõ về chúng trước khi tham dự buổi phỏng vấn JavaScript

5) Hiểu về objects (Object.freeze, Object.seal)

Hầu hết chúng ta biết các objects kiểu này:

var marks = {physics: 98, maths:95, chemistry: 91};

Nó là một bản đồ lưu trữ các cặp Key, Value. Các đối tượng JavaScripts có một thuộc tính đặc biệt là lưu trữ bất kỳ thứ gì dưới dạng giá trị. Nó có nghĩa là chúng ta có thể lưu trữ một danh sách, một đối tượng khác, một hàm, v.v. dưới dạng một giá trị. Vậy có gì không lưu trữ được?

Bạn có thể tạo một object theo những cách sau:

var marks = {};
var marks = new Object();

Bạn có thể dễ dàng chuyển đổi một đối tượng nhất định thành một chuỗi JSON và cũng có thể đảo ngược nó trở lại bằng cách sử dụng các phương thức phân tích cú pháp và chuỗi của đối tượng (object’s stringify and parse methods) JSON tương ứng.

// returns "{"physics":98,"maths":95,"chemistry":91}"
JSON.stringify(marks);

// Get object from string
JSON.parse('{"physics":98,"maths":95,"chemistry":91}');

Vậy một vài điều về đối tượng mà bạn nên biết:

  • Lặp lại đối tượng rất dễ dàng, sử dụng Object.key:
var highScore = 0; for (i of Object.keys(marks)) {
if (marks[i] > highScore)
highScore = marks[i];
}
  • Object.values trả về danh sách các giá trị của một object
  • Những hàm quan trọng khác của object:
  • Object.prototype cung cấp các chức năng quan trọng hơn có nhiều ứng dụng. Một số trong số đó là:
    • Object.prototype.hasOwnProperty rất hữu ích để tìm hiểu xem một thuộc tính / khóa nhất định có tồn tại trong một đối tượng hay không.
marks.hasOwnProperty("physics"); // returns true
marks.hasOwnProperty("greek"); // returns false
    • Object.prototype.instanceof đánh giá xem một đối tượng nhất định có phải là kiểu của một nguyên mẫu cụ thể hay không (chúng ta sẽ thấy chúng trong phần tiếp theo, chúng là các hàm).
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}var newCar = new Car('Honda', 'City', 2007);console.log(newCar instanceof Car); // returns true
  • Object.freeze cho phép chúng ta đóng băng một đối tượng để không thể sửa đổi các thuộc tính hiện có.
var marks = {physics: 98, maths:95, chemistry: 91};
finalizedMarks = Object.freeze(marks);
finalizedMarks["physics"] = 86; // throws error in strict mode
console.log(marks); // {physics: 98, maths: 95, chemistry: 91}

Ở đây chúng ta đang cố gắng sửa đổi giá trị của thuộc tính vật lý sau khi đóng băng đối tượng. Nhưng, JavaScript sẽ không cho phép làm điều đó. Chúng ta có thể tìm xem một đối tượng nhất định có bị đóng băng hay không như thế này.

Object.isFrozen(finalizedMarks); // returns true

  • Object.seal hơi khác so với việc đóng băng. Nó cho phép các thuộc tính có thể định cấu hình nhưng sẽ không cho phép thêm hoặc xóa thuộc tính mới hoặc các thuộc tính.
var marks = {physics: 98, maths:95, chemistry: 91};
Object.seal(marks);delete marks.chemistry; // returns false as operation failed
marks.physics = 95; // Works!
marks.greek = 86; // Will not add a new property

Chúng tôi cũng có thể kiểm tra xem một đối tượng nhất định có được niêm phong (seal) hay không bằng cách sử dụng:

Object.isSealed(marks); // returns true

Ngoài ra có nhiều function/methods quan trọng khác có sẵn trên global Object function. Bạn có thể tìm thêm thông tin về Object ở đây.

6) Nắm vững về Prototypical Inheritance

Trong JavaScript truyền thống, có khái niệm kế thừa (inheritance) trong một lớp ngụy trang. Đó là bằng cách sử dụng một kỹ thuật tạo mẫu (prototyping). Tất cả các cú pháp class mới mà bạn thấy trong ES5, ES6 chỉ là một lớp phủ đường (syntactical sugar) cú pháp cho OOP nguyên mẫu cơ bản. Việc tạo một lớp được thực hiện bằng cách sử dụng một hàm trong JavaScript.

var animalGroups = {
MAMMAL: 1,
REPTILE: 2,
AMPHIBIAN: 3,
INVERTEBRATE: 4
};function Animal(name, type) {
this.name = name;
this.type = type;
}var dog = new Animal("dog", animalGroups.MAMMAL);
var crocodile = new Animal("crocodile", animalGroups.REPTILE);

Ở đây chúng ta đang tạo các objects cho class (sử dụng new keyword). Chúng ta có thể thêm các phương thức cho một lớp (hàm) nhất định như thế này. Đính kèm một phương thức lớp như thế này:

Animal.prototype.shout = function () {
console.log (this.name + 'is' + this.sound + 'ing ...');
}

Bạn có thể nghi ngờ. Không có thuộc tính sound trong class. Đúng! hầu như không có sound property nào được xác định. Điều đó được dự định sẽ được truyền bởi các lớp con kế thừa lớp trên.

Trong JavaScript, tính kế thừa đạt được như thế này.

function Dog(name, type) {
Animal.call(this, name, type);
this.sound = "bow";
}

Ta định nghĩa một hàm cụ thể hơn được gọi là Dog. Ở đây, để kế thừa lớp Animal, chúng ta cần thực hiện hàm gọi với việc truyền this và các đối số (arguments) khác. Chúng ta có thể tạo ra một German Shepard như thế này.

var pet = Dog("germanShepard", animalGroups.MAMMAL);
console.log(pet); // returns Dog {name: "germanShepard", type: 1, sound: "bow"}

Chúng tôi không gán tên (name) và loại (type) cho hàm con, chúng ta gọi siêu hàm Animal và thiết lập các thuộc tính tương ứng. Con vật cưng đang có các thuộc tính (tên, loại) của cha mẹ. Nhưng các method thì sao? Chúng có được kế thừa không? Hãy xem nào

pet.shout(); // Throws error

Cái gì vậy ? tại sao điều đó xảy ra? Điều này xảy ra bởi vì chúng ta không yêu cầu JavaScript kế thừa các phương thức của lớp cha. Vậy thì làm thế nào để khắc phục điều đó?

// Link prototype chains
Dog.prototype = Object.create(Animal.prototype);
var pet = new Dog("germanShepard", animalGroups.MAMMAL);

// Now shout method is available
pet.shout(); // germanShepard is bowing...

Bây giờ shoud method đã có. Chúng ta có thể kiểm tra lớp của đối tượng đã cho trong JavaScript là gì bằng cách sử dụng hàm object.constructor. Chúng ta hãy kiểm tra xem lớp pet của chúng tôi là gì.

pet.constructor; // returns Animal

Nó thật mơ hồ. Animal là một lớp cha. Nhưng pet (loại vật) nuôi chính xác là loại nào? Nó là một Dog type. Điều này xảy ra do hàm tạo của lớp Dog.

Dog.prototype.constructor; // returns Animal

Nó là Animal. Chúng ta nên đặt nó thành chính lớp Dog để tất cả các cá thể (đối tượng) của lớp phải cung cấp tên lớp chính xác nơi nó thuộc về.

Dog.prototype.constructor = Dog;

Có 5 điều bạn cần nhớ về prototypical inheritance.

  • Class properties được ràng buộc bằng cách sử dụng this
  • Các Class methods được ràng buộc bằng cách sử dụng prototype object
  • Để kế thừa các thuộc tính, hãy sử dụng hàm gọi (call function) truyền đối tượng này
  • Để kế thừa các phương thức, hãy sử dụng Object.create để liên kết các nguyên mẫu (prototypes) của cha và con
  • Luôn đặt hàm tạo lớp con cho chính nó để nhận được danh tính phù hợp của các đối tượng của nó

7) Hiểu rõ hàm callbacks và đối tượng promises

Callbacks là các hàm được thực thi sau khi thực hiện xong một thao tác I / O. Thao tác I / O mất nhiều thời gian có thể chặn mã không cho phép thực thi thêm trong Python / Ruby. Nhưng trong JavaScript, do thực thi không đồng bộ được phép, chúng ta có thể cung cấp các lệnh gọi lại cho các hàm không đồng bộ. Ví dụ là một lệnh gọi AJAX (XMLHttpRequest) từ trình duyệt đến máy chủ, các sự kiện do chuột tạo ra. bàn phím, v.v. Ví dụ:

function reqListener () {
console.log(this.responseText);
}

var req = new XMLHttpRequest();
req.addEventListener("load", reqListener);
req.open("GET", "http://www.example.org/example.txt");
req.send();

Ở đây reqListener là lệnh gọi lại sẽ được thực thi khi một yêu cầu GET tới được phản hồi lại thành công.

Promises là các trình bao bọc gọn gàng cho các lệnh callbacks cho phép chúng ta mã không đồng bộ. Đây cũng là một phần quan trọng cần được biết đến trong JS. Promises là một đại diện cho một giá trị không nhất thiết phải biết khi Promises được tạo. Nó cho phép bạn liên kết các trình xử lý với giá trị thành công cuối cùng của hành động không đồng bộ hoặc lý do thất bại. Điều này cho phép các phương thức không đồng bộ trả về giá trị giống như các phương thức đồng bộ: thay vì giá trị cuối cùng, phương thức không đồng bộ trả về một Promises cho giá trị tại một thời điểm nào đó trong tương lai. Bạn có thể tìm hiểu thêm về Promises tại đây

Tạo và dùng promises

Tạo và dùng promises. Ảnh Medium

8) Hiểu rõ về regular expressions

Biểu thức chính quy (regular expressions) hay còn được gọi tắt là Regex hay RegExp, là một cách để biểu diễn khuôn mẫu của string. Biểu thức chính quy có nhiều ứng dụng. Xử lý văn bản, thực thi các quy tắc về đầu vào của người dùng, v.v. Một JavaScript developer nên biết cách thực hiện Regex cơ bản và giải quyết các vấn đề trong thực tế (chứ không phải chỉ chuẩn bị cho phỏng vấn JavaScript). Regex là một khái niệm phổ quát. Hãy xem cách chúng ta có thể làm điều đó từ JS. 

Chúng ta có thể tạo một regular expression bằng cách sử dụng:

var re = /ar/;
var re = new RegExp('ar'); // This too works

Biểu thức chính quy ở trên là một biểu thức phù hợp với tập hợp các chuỗi đã cho. Khi một regex được xác định, chúng ta có thể thử điều chỉnh và xem chuỗi phù hợp. chúng ta có thể so khớp các chuỗi bằng cách sử dụng hàm exec.

re.exec("car"); // returns ["ar", index: 1, input: "car"]
re.exec("cab"); // returns null

Có một số lớp ký tự đặc biệt cho phép chúng ta viết các biểu thức chính quy phức tạp.

Có nhiều loại phần tử (elements) trong RegEx. Một số trong số đó là:

  • Các ký tự Ví dụ: \ w – Chữ và số, \ d – Thập phân, \ D – Không thập phân
  • Các lớp ký tự Ví dụ: [x-y] trong phạm vi x đến y, [^ x] không phải x
  • Các định lượng Ví dụ: +,?, * (greedy and lazy matchers)
  • Ranh giới Ví dụ: ^ (đầu vào), $ (cuối đầu vào)

Bạn có thể xem thêm về các ký tự đặc biệt dùng trong RegEx tại đây

Sử dụng những điều trên, hãy minh họa một vài ví dụ.

/* Character class */
var re1 = /[AEIOU]/;
re1.exec("Oval"); // returns ["O", index: 0, input: "Oval"]
re1.exec("2456"); // nullvar
re2 = /[1-9]/;
re2.exec('mp4'); // returns ["4", index: 2, input: "mp4"]
/* Characters */
var re4 = /\d\D\w/;
re4.exec('1232W2sdf'); // returns ["2W2", index: 3, input: "1232W2sdf"]
re4.exec('W3q'); // returns null
/* Boundaries */
var re5 = /^\d\D\w/;
re5.exec('2W34'); // returns ["2W3", index: 0, input: "2W34"]
re5.exec('W34567'); // returns null
var re6 = /^[0-9]{5}-[0-9]{5}-[0-9]{5}$/;
re6.exec('23451-45242-99078'); // returns ["23451-45242-99078", index: 0, input: "23451-45242-99078"]
re6.exec('23451-abcd-efgh-ijkl'); // returns null
/* Quantifiers */
var re7 = /\d+\D+$/;
re7.exec('2abcd'); // returns ["2abcd", index: 0, input: "2abcd"]
re7.exec('23'); // returns null
re7.exec('2abcd3'); // returns null
var re8 = /<([\w]+).*>(.*?)<\/\1>/;
re8.exec('<p>Hello JS developer</p>'); //returns ["<p>Hello JS developer</p>", "p", "Hello JS developer", index: 0, input: "<p>Hello JS developer</p>"]

Để biết thêm chi tiết về regex, hãy xem trang cheatsheet này.

Cùng với exec, có các chức năng khác như match, searchreplace có sẵn để tìm một chuỗi trong chuỗi khác bằng cách sử dụng biểu thức chính quy. Nhưng các hàm này nên được sử dụng trên chính chuỗi.

"2345-678r9".match(/[a-z A-Z]/); // returns ["r", index: 8, input: "2345-678r9"]
"2345-678r9".replace(/[a-z A-Z]/, ""); // returns 2345-6789

Không chỉ quan trọng để chuẩn bị cho buổi phỏng vấn JavaScript, Regex còn là một chủ đề quan trọng cần được hiểu bởi các developer để giải quyết các vấn đề phức tạp một cách dễ dàng.

9) Hiểu về hàm Map, Reduce và Filter

Lập trình hàm là một chủ đề thảo luận nhiều những ngày này. Nhiều ngôn ngữ lập trình đang đưa các khái niệm chức năng như lambdas vào các phiên bản mới hơn của chúng (Ví dụ: Java> 7). Trong JavaScript, cấu trúc hỗ trợ cho lập trình hàm tồn tại trong một thời gian dài. Có ba chức năng chính chúng ta cần tìm hiểu sâu. Các hàm toán học nhận một số đầu vào và đầu ra trả về. Một hàm thuần túy luôn trả về cùng một đầu ra cho đầu vào đã cho. Các hàm chúng ta thảo luận bây giờ cũng đáp ứng sự thuần khiết.

Hàm map

Hàm map có sẵn trên một mảng JavaScript. Sử dụng hàm này, chúng ta có thể nhận được một mảng mới bằng cách áp dụng một hàm biến đổi trên mỗi và mọi phần tử trong mảng. Cú pháp chung của array map operation trong JS là:

arr.map((elem){
process(elem)
return processedValue
}) // returns new array with each element processed

Giả sử có một vài ký tự không mong muốn được nhập vào các khóa nối tiếp mà chúng ta đang làm việc gần đây. Chúng ta cần loại bỏ chúng. Thay vì xóa ký tự bằng cách lặp và tìm, chúng ta có thể sử dụng hàm map để thực hiện cùng một thao tác và lấy mảng kết quả.

var data = ["2345-34r", "2e345-211", "543-67i4", "346-598"];
var re = /[a-z A-Z]/;
var cleanedData = data.map((elem) => {return elem.replace(re, "")});
console.log(cleanedData); // ["2345-34", "2345-211", "543-674", "346-598"]

Lưu ý: Chúng ta đang sử dụng arrow syntax để định nghĩa hàm trong JavaScript ES6

Hàm map nhận một hàm làm đối số. Hàm đó có một đối số. Đối số đó được chọn từ mảng. Chúng ta cần trả về phần tử đã xử lý và điều đó sẽ được áp dụng cho tất cả các phần tử trong mảng.

Hàm Reduce

Hàm Reduce làm giảm một danh sách nhất định thành một kết quả cuối cùng. Chúng ta cũng có thể làm điều tương tự bằng cách lặp lại mảng và lưu kết quả trung gian trong một biến. Nhưng đây là một cách đơn giản hơn để giảm một mảng thành một giá trị. Cú pháp chung cho tác vụ reduce trong JS là:

arr.reduce((accumulator,
currentValue,
currentIndex) => {
process(accumulator, currentValue)
return intermediateValue/finalValue
}, initialAccumulatorValue) // returns reduced value

Hàm accumulator lưu trữ giá trị trung gian và giá trị cuối cùng. currentIndexcurrentValue lần lượt là chỉ số và giá trị của phần tử từ mảng. initialAccumulatorValue chuyển giá trị đó đến đối số của accumulator.

Một ứng dụng thực tế của hàm reduce là làm phẳng một mảng các mảng. Làm phẳng là chuyển đổi các mảng bên trong thành một mảng duy nhất. Ví dụ:

var arr = [[1, 2], [3, 4], [5, 6]];
var flattenedArray = [1, 2, 3, 4, 5, 6];

Ta có thể thực hiện được điều này bằng cách lặp lại. Nhưng sử dụng reduce sẽ đơn giản hơn rất nhiều.

var flattenedArray = arr.reduce((accumulator, currentValue) => {
return accumulator.concat(currentValue);
}, []); // returns [1, 2, 3, 4, 5, 6]

Hàm filter

Đây là loại khái niệm lập trình hàm thứ ba. Nó gần giống với hàm map vì cũng xử lý từng phần tử trong mảng và cuối cùng trả về một mảng khác (không trả về giá trị như trong Reduce). Độ dài của mảng được lọc có thể nhỏ hơn hoặc bằng mảng ban đầu. Cú pháp chung của hàm filter:

arr.filter((elem) => {
return true/false
})

Ở đây elem là phần tử dữ liệu của mảng và true / false cần được trả về từ hàm để chỉ ra việc bao gồm / loại trừ phần tử được lọc. Ví dụ phổ biến là lọc mảng các từ bắt đầu và kết thúc với các điều kiện đã cho. Giả sử, chúng ta cần lọc một mảng các từ bắt đầu bằng t và kết thúc bằng r.

var words = ["tiger", "toast", "boat", "tumor", "track", "bridge"]
var newData = words.filter((elem) => {
return elem.startsWith('t') && elem.endsWith('r') ? true:false;
}); // returns ["tiger", "tumor"]

Bạn cần nắm rõ ba hàm này để đảm bảo áp dụng đúng và trả lời bất cứ các câu hỏi phỏng vấn liên quan trong về JavaScript. Như bạn thấy, mảng ban đầu không bị thay đổi trong cả ba trường hợp, điều này chứng tỏ sự thuần khiết của các hàm này.

10) Xử lý Error trong JavaScript

Đây là phần ít được các JavaScript developer quan tâm nhất. Một cách tiếp cận phát triển nên áp dụng là  luôn bao bọc cẩn thận code JS xung quanh các khối try / catch.

Nicholas C. Zakas, một kỹ sư giao diện người dùng tại Yahoo đã nói “Hãy luôn cho rằng mã của bạn sẽ thất bại. Các event có thể không được xử lý đúng cách! Hãy thông báo cho máy chủ. Hãy vứt bỏ lỗi của chính mình ”.

Trong JavaScript, bất cứ khi nào chúng ta viết code một cách ngẫu nhiên, mọi thứ có thể không thành công.

$("button").click(function(){
$.ajax({url: "user.json", success: function(result){
updateUI(result["posts"]);
}});
});

Ở đây chúng ta đang rơi vào bẫy khi cho rằng kết quả luôn luôn là một đối tượng JSON. Đôi khi máy chủ có thể gặp sự cố và null sẽ được trả về thay vì kết quả. Trong trường hợp đó, null [“posts”] sẽ gây ra lỗi. Việc xử lý thích hợp có thể như sau:

$("button").click(function(){
$.ajax({url: "user.json", success: function(result){

try {
updateUI(result["posts"]);
}
catch(e) {
// Custom functions
logError();
flashInfoMessage();
}
}});
});

Hàm logError nhằm thông báo lỗi trở lại máy chủ. Hàm thứ hai flashInfoMessage là hàm hiển thị thông báo thân thiện với người dùng như “Dịch vụ hiện không khả dụng”, v.v.

Nicholas nói rằng hãy ném lỗi theo cách thủ công bất cứ khi nào bạn cảm thấy điều gì đó bất ngờ sắp xảy ra. Phân biệt lỗi nghiêm trọng và lỗi không nghiêm trọng. Lỗi trên có liên quan đến backend server gặp sự cố nghiêm trọng. Tại đó, bạn nên thông báo cho khách hàng rằng dịch vụ ngừng hoạt động do một lý do nào đó. Trong một số trường hợp, nó có thể không gây nghiêm trọng nhưng tốt hơn nên thông báo cho sever về điều này. Để tạo code như vậy, trước tiên, hãy tạo ra một lỗi, bắt nó với sự kiện lỗi ở window object level, sau đó thực hiện một lệnh gọi API để ghi lại thông báo đó vào máy chủ.

reportErrorToServer = function (error) {
$.ajax({type: "POST",
url: "http://api.xyz.com/report",
data: error,
success: function (result) {}
});
}
// Window error event
window.addEventListener('error', function (e) {
reportErrorToServer({message: e.message})
})}
function mainLogic() {
// Somewhere you feel like fishy
throw new Error("user feeds are having fewer fields than expected...");
}

Đoạn code trên thực hiện 3 điều:

  1. Theo dõi Errors ở window level
  2. Bất cứ khi nào có lỗi xuất hiện, thực hiện việc gọi API
  3. Log lỗi trên server

Bạn cũng có thể sử dụng hàm Boolean (ES5, ES6) để kiểm tra xem một biến có hợp lệ và không null (hoặc) không xác định hay không trước khi tiếp tục.

if (Boolean(someVariable)) {
// use variable now
} else {
throw new Error("Custom message")
}

Luôn nghĩ cách xử lý lỗi, không phải ở trình duyệt mà là ở chính bạn. Mọi thứ có thể thất bại!

11) Những vấn đề khác bạn cần nắm vững cho buổi phỏng vấn JavaScript: Hoisting, Event Bubbling

Tất cả các khái niệm đã trình bày này là quan trọng đối với một nhà phát triển JavaScript, không chỉ cho buổi phỏng vấn mà cho công việc hàng ngày của một JavaScript developer. Tuy nhiên, có một số khái niệm và công cụ khác thực sự hữu ích. Đó là cách JavaScript hoạt động trong trình duyệt. Vậy HoistingEvent Bubbling là gì?

Hoisting

Hoisting là một quá trình đẩy các biến đã khai báo lên đầu chương trình trong khi chạy nó. Ví dụ:

doSomething(foo); // used before
var foo; // declared later

Khi bạn thực hiện điều trên bằng một ngôn ngữ kịch bản (scripting language) như Python, nó sẽ xuất hiện lỗi. Trước tiên bạn cần định nghĩa và sử dụng nó. Mặc dù JavaScript là một ngôn ngữ kịch bản, nhưng nó sử cơ chế hoisting. Trong cơ chế này, một máy ảo JavaScript thực hiện hai việc trong khi chạy một chương trình:

  • Đầu tiên quét chương trình, thu thập tất cả các khai báo biến và hàm và gán không gian bộ nhớ cho nó.
  • Chạy chương trình bằng cách điền các giá trị biến được gán bất kỳ, nếu không, điền undefined

Trong đoạn code trên, console.log in ra nội dung “undefined”. Đó là vì trong biến truyền đầu tiên, foo được thu thập. VM tìm kiếm bất kỳ giá trị nào được xác định cho biến foo. Việc lưu trữ này có thể dẫn đến nhiều tình huống trong đó code có thể tạo ra lỗi ở một số nơi và sử dụng âm thầm không xác định ở một số nơi khác. Bạn cần phải nắm rõ về hoisting để trách những tình huống đó. Bạn hãy tìm hiểu thêm và các ví dụ trong tài liệu về Hoisting này để trả lời tốt các câu hỏi phỏng vấn JavaScript.

Event Bubbling

Theo Arun P, một kỹ sư phần mềm cao cấp:

“Event Bubbling (Tạo bọt) và Capturing (ghi lại) là hai cách truyền sự kiện trong HTML DOM API khi một sự kiện xảy ra trong một phần tử bên trong phần tử khác và cả hai phần tử đã đăng ký một trình xử lý cho sự kiện đó. Chế độ lan truyền sự kiện (event propagation mode) xác định thứ tự các phần tử nhận sự kiện ”.

Với bubbling, sự kiện đầu tiên được phần tử trong cùng nắm bắt và xử lý, sau đó được truyền đến các phần tử bên ngoài. Với capturing, quá trình này diễn ra ngược lại. Chúng ta thường đính kèm một sự kiện vào một trình xử lý bằng cách sử dụng hàm addEventListener.

addEventListener("click", handler, useCapture=false)

Đối số thứ ba useCapture là chìa khóa. Giá trị mặc định là false. Vì vậy, nó sẽ là một bubbling model trong đó sự kiện được xử lý bởi phần tử trong cùng đầu tiên và lan truyền ra bên ngoài cho đến khi đến phần tử mẹ. Nếu đối số đó là true, thì đó là capturing model.

Ví dụ về Bubbling Model:

<div onClick="divHandler()">
<ul onClick="ulHandler">
<li id="foo"></li>
</ul>
</div>
<script>
function handler() {
// do something here
}
function divHandler(){}
function ulHandler(){}
document.getElementById("foo").addEventListener("click", handler)
</script>

Khi chúng ta nhấp vào phần tử danh sách, thứ tự thực hiện của các trình xử lý giống như thế này trong bubbling model.

handler() => ulHandler() => divHandler()

Bubbling model trong phỏng vấn JavaScript

Trong sơ đồ trên, các trình xử lý đang chuyển tuần tự ra bên ngoài. Tương tự như vậy, một mô hình capturing sẽ cố gắng kích hoạt các sự kiện vào trong từ phần tử gốc đến phần tử được nhấp vào. Bây giờ ta thay đổi trong đoạn mã trên.

document.getElementById("foo").addEventListener("click", handler, true)

Thứ tự thực hiện của các trình xử lý sau đó sẽ là:

divHandler => ulHandler() => handler()

Capturing model trong JavaScript

Bạn cần nắm rõ các event bubbling (cho dù hướng ra parents hay vào child) đúng cách để triển khai các giao diện người dùng (UI) nhằm tránh bất kỳ hành vi không mong muốn nào. Nó cũng sẽ giúp cho bạn thành công trong buổi phỏng vấn về JavaScript

Kết luận

Đây là những khái niệm cơ bản nhưng rất quan trong JavaScript. Như đã đề cập ban đầu, ngoài những khái niệm này thì kinh nghiệm làm việc và kiến thức của bạn, sự chuẩn bị tốt sẽ giúp bạn vượt qua một cuộc phỏng vấn JavaScript. Luôn không ngừng học hỏi. Theo dõi những phát triển mới nhất của ngôn ngữ lập trình này . Tìm hiểu sâu hơn về các khía cạnh khác nhau của JavaScript, thực các bài kiểm tra, v.v. Ngoài ra, không có cuộc phỏng vấn nào thành công nếu không nắm vững Cấu trúc dữ liệu & Thuật toán. Bạn có thể xem tren git repo của Oleksii Trekhleb, nơi bạn có thể tìm tất cả các thuật toán chuẩn bị phỏng vấn với JavaScript. Hãy tìm hiểu.

Nguồn tham khảo chính của bài này

1/Bài của Naren Yellavula trên Medium: https://medium.com/dev-bits/a-perfect-guide-for-cracking-a-javascript-interview-a-developers-perspective-23a5c0fa4d0d

2/https://scotch.io/courses/10-need-to-know-javascript-concepts/scope

3/https://freetuts.net/bien-toan-cuc-va-bien-cuc-bo-trong-javascript-377.html

4/https://completejavascript.com/ke-thua-co-ban-trong-javascript

5/https://viblo.asia/p/bieu-thuc-chinh-quy-regex-trong-javascript-QpmleQ9mlrd

6/JavaScript — Dynamic client-side scripting: https://developer.mozilla.org/en-US/docs/Learn/JavaScript

Bạn có biết?


tham gia cộng đồng ITguru trên Linkedin, Facebook và các kênh mạng xã hội khác có thể giúp bạn nhanh chóng tìm được những chủ đề phát triển nghề nghiệp và cập nhật thông tin về việc làm IT mới nhất

Linkedin Page: https://bit.ly/LinkedinITguru
Facebook Group: https://bit.ly/ITguruvn
cơ hội việc làm IT : ITguru.vn

Bạn đánh giá bài viết thế nào?

Average rating 5 / 5. Vote count: 7

No votes so far! Be the first to rate this post.