Skip to content

Interviews - javascript

All the javascript interview question.

C1:

Trong javascript có bao nhiêu phạm vi của biến, hãy giải thích nó?

Xem đáp án

Ban đầu lúc được thiết kế thì Javascript chỉ có 2 phạm vi duy nhất là phạm vị toàn cục (global scope) và phạm vi địa phương (local scope).

Global scope

Các biến có phạm vi toàn cục sẽ có thể truy xuất ở bất kì đâu. Ví dụ đơn giản:

var a = 10;

function doSomething(){
  console.log(a); // có thể truy cập a
}

function doAnotherThing(){
  console.log(a); // có thể truy cập a

}

doSomething();
doAnotherThing();

Local scope

Các biến chỉ tồn tại bên trong các hàm (function), local scope còn được gọi là function scope vì lẽ này. Mỗi hàm có phạm vi cục bộ riêng của nó, do đó một hàm không thể truy cập các biến cục bộ của một hàm khác. Ví dụ:

function doSomething(){
  var a = 10;
}

function doAnotherThing(){
 console.log(a); // không thể có thể truy cập biến A. Lỗi: ReferenceError: a is not defined
}

doAnotherThing();

Từ năm 2015 trở đi với ES6

ES6 bổ sung thêm từ khóa let cho javascript. Các biến khai báo với từ khóa let sẽ có phạm vị khối (block scope).

Phạm vị khối (block scope) là một biến được khai báo trong 1 khối lệnh nào đó, ví dụ: câu lệnh điều kiện (if) hoặc vòng lặp (for). Thực sự thì phạm vi khối không có gì lạ đối với các ngôn ngữ hướng đối tượng như Java, C#, tuy nhiên đối với Javascript thì hơi “dị”?

Ta xem ví dụ bên dưới:

if(true){
  let a = 10;
  var b = 20;
}
//Có thể truy cập b nhưng không thể truy cập a

console.log(b); // in ra 10
console.log(a); // Lỗi: a is not defined

Trong ví dụ trên, biến a khai báo với let sẽ có phạm vị khối lệnh. Do đó, bên ngoài khối lệnh if thì không thể truy xuất được. Trong khi đó, biến b khai báo bằng từ khóa var sẽ có phạm vị hàm, đo đó, vẫn có thể truy xuất bên ngoài khối lệnh if.

entry

C2:

So sánh ===== trong Javascript

Xem đáp án

JavaScript có 2 loại so sánh:

  • Strict comparison (===) hay còn gọi là so sanh nghiêm ngặt, nó sẽ kiểm tra giá trị bằng nhau mà không tự động ép kiểu.
  • Abstract comparison (==) hay còn gọi là so sanh không nghiêm ngặt, nó sẽ kiểm tra giá trị bằng nhau có tự động ép kiểu.

Ví dụ:

var a = "42";
var b = 42;

a == b; // true
a === b; // false

Bạn có thể xem chi tiết hơn về so sánh trong javascript ở đây, vì nó sẽ có rất nhiều trường hợp phi logic so sánh trong javascript

Một số quy tắc so sánh đơn giản:

  • Nếu giá trị của một trong hai bên của phép so sánh là true hoặc false, hãy tránh == và sử dụng ===.

  • Nếu giá trị của một trong hai bên của phép so sánh là các giá trị cụ thể này (0, "" hoặc [] — mảng trống), hãy tránh == và sử dụng ===.

  • Trong tất cả các trường hợp khác, bạn có thể yên tâm sử dụng ==. Nó không chỉ an toàn mà trong nhiều trường hợp, nó đơn giản hóa mã của bạn giúp dễ đọc hơn.

entry

C3:

Sự khác biệt của biến dùng var, letconst

Xem đáp án

Từ phiên bản ES6, từ khoá let và const được thêm vào cho khai báo biến.

keywordconstletvar
global scopenonoyes
function scopeyesyesyes
block scopeyesyesno
can be reassignednoyesyes

Ta thử xem các ví dụ:

var variable1 = 23;

let variable2 = 89;

function catchValues(){
  console.log(variable1);
  console.log(variable2);

// Both the variables can be accessed anywhere since they are declared in the global scope
}

window.variable1; // Returns the value 23

window.variable2; // Returns undefined   

Các biến được khai báo với từ khóa let trong global scope sẽ hoạt động giống như biến được khai báo với từ khóa var trong global scope.

Các biến được khai báo trong global scope với từ khóa var và let có thể được truy cập từ bất kỳ đâu trong code.

Nhưng, có một sự khác biệt!

Các biến được khai báo với từ khóa var trong global scope được thêm vào đối tượng window/global. Do đó, chúng có thể được truy cập bằng window.variableName.

Trong khi đó, các biến được khai báo với từ khóa let không được thêm vào đối tượng toàn cục, do đó, việc cố gắng truy cập các biến đó bằng cách sử dụng window.variableName sẽ dẫn đến lỗi.

var và let ở function scope

function varVsLetFunction(){
  let awesomeCar1 = "Audi";
  var awesomeCar2 = "Mercedes";
}

console.log(awesomeCar1); // Throws an error
console.log(awesomeCar2); // Throws an error

Các biến được khai báo trong function scope bằng cách sử dụng varlet hoạt động hoàn toàn giống nhau, nghĩa là chúng không thể được truy cập từ bên ngoài phạm vi.

{
  var variable3 = [1, 2, 3, 4];
}

console.log(variable3); // Outputs [1,2,3,4]

{
  let variable4 = [6, 55, -1, 2];
}

console.log(variable4); // Throws error

for(let i = 0; i < 2; i++){
  //Do something
}

console.log(i); // Throws error

for(var j = 0; j < 2; i++){
  // Do something
}

console.log(j) // Outputs 2

Trong javascript, một khối có nghĩa là code được viết bên trong dấu ngoặc nhọn {}.

Các biến được khai báo với từ khóa var không có block scope. Nó có nghĩa là một biến được khai báo trong block scope {} với từ khóa var cũng giống như khai báo biến trong global scope.

Các biến được khai báo với từ khóa let bên trong block scope không thể được truy cập từ bên ngoài khối.

const

Các biến với từ khóa const hoạt động chính xác như một biến được khai báo với từ khóa let chỉ có một điểm khác biệt, bất kỳ biến nào được khai báo với từ khóa const đều là hằng số, tức là không thể được gán lại.

const x = {name:"Vivek"};

x = {address: "India"}; // Throws an error

x.name = "Nikhil"; // No error is thrown

const y = 23;

y = 44; // Throws an error

Trong đoạn code trên, mặc dù chúng ta có thể thay đổi giá trị của một thuộc tính bên trong biến được khai báo với từ khóa const, nhưng chúng ta không thể gán lại hoàn toàn chính biến đó.

entry

C4:

Cho biết kết quả của đoạn code dưới đây?

  console.log(typeof typeof 1);
Xem đáp án

Kết quả: string

Giải thích:

typeof 1 là Number và typeof Number là string

entry

C5:

Object trong Javascript là gì?

Xem đáp án

Trong JavaScript, đối tượng là một thực thể độc lập, có thể được xem như một bản ghi có chứa thông tin về một đối tượng cụ thể. Tương tự như cách một cái cốc có thể có những đặc điểm riêng của nó như màu sắc, thiết kế, trọng lượng, chất liệu, trong JavaScript, đối tượng cũng có thể có những thuộc tính định nghĩa sự đặc trưng của chúng.

Một đối tượng trong JavaScript được tạo ra bằng cách tạo ra một tập hợp các cặp tên - giá trị, trong đó tên được coi là một chuỗi duy nhất và giá trị có thể là bất kỳ giá trị nào, bao gồm cả các đối tượng khác và các hàm.

Để tạo một đối tượng trong JavaScript, có ba cách thông dụng như sau:

Cách đầu tiên là sử dụng cú pháp literal để tạo một đối tượng mới bằng cách khai báo biến và gán giá trị là một đối tượng trống {}:

const dog = {};

Cách thứ hai là sử dụng hàm tạo Object() để tạo một đối tượng mới:

const cat = new Object();

Cách thứ ba là sử dụng phương thức tĩnh Object.create() để tạo một đối tượng mới, dựa trên một đối tượng đã có sẵn (gọi là đối tượng prototype):

const horse = Object.create({});

entry

C6:

Sự khác biệt giữa các kiểu dữ liệu trong JavaScript?

Xem đáp án

Trong JavaScript các kiểu dữ liệu được chia làm hai loại là kiểu nguyên thuỷ và đối tượng. Để biết kiểu dữ liệu của các biến JavaScript, ta có thể sử dụng typeof.

String - biểu diễn một mảng ký tự hay một chuỗi. Kiểu chuỗi trong javascript có thể sử dụng một cặp dấu ngoặc kép hoặc dấu ngoặc kép đơn.

var str = "Vivek Singh Bisht"; //sử dụng dấu ngoặc kép
var str2 = "John Doe"; // sử dụng dấu ngoặc đơn

Number - biểu diễn cả số nguyên và số thực.

var x = 3; // số nguyên
var y = 3.6; // số thực

BigInt - kiểu dữ liệu này được sử dụng để lưu trữ các số vượt quá giới hạn của kiểu dữ liệu Number. Nó có thể lưu trữ các số nguyên lớn và được biểu diễn bằng cách thêm “n” vào một chữ số nguyên.

var bigInteger = 234567890123456789012345678901234567890;

Boolean - kiểu luận lý, có hai giá trị là truefalse. Thường được dùng với điều kiện.

var a = 2;
var b = 3;
var c = 2;
(a == b)(
  // trả về false
  a == c,
); // trả về true

undefined - khi giá trị của một biến là không xác định.

var x; // giá trị của x là undefined
var y = undefined; // ta cũng có thể gán một biến là undefined

null - biểu diễn giá trị null. Vì JavaScript là case-sensitive, null sẽ không giống với Null, NULL, hoặc bất kỳ biến thể khác.

var z = null;

Symbol - mới được giới thiệu trong ES6. Nó lưu trữ các giá trị duy nhất và ẩn danh.

var symbol1 = Symbol("symbol");

Sử dụng typeof để tìm kiểu nguyên thuỷ:

typeof "John Doe"; // Returns "string"
typeof 3.14; // Returns "number"
typeof true; // Returns "boolean"
typeof 234567890123456789012345678901234567890n; // Returns bigint
typeof undefined; // Returns "undefined"
typeof null; // Returns "object" (đặc trưng của JavaScript)
typeof Symbol("symbol"); // Returns Symbol

Trong JavaScript nếu dữ liệu không phải là kiểu nguyên thuỷ thì tất cả đều là object.

Object - dùng để lưu trữ tập hợp dữ liệu

// Tập hợp dữ liệu dạng key-value

var obj1 = {
  x: 43,
  y: "Hello world!",
  z: function () {
    return this.x;
  },
};

// Tập hợp dữ liệu dạng danh sách

var array1 = [5, "Hello", true, 4.1];

entry

C7:

Kể các cách khai báo 1 biến trong Javascript?

Xem đáp án

Trong JavaScript, có ba cách khai báo biến:

var: là cách khai báo biến truyền thống trong JavaScript. Biến được khai báo bằng từ khóa var có thể được truy cập ở bất kỳ đâu trong phạm vi của hàm hoặc khối được khai báo. Ví dụ:

var myVariable = "Hello World!";

let: là một cách khai báo biến mới được giới thiệu trong phiên bản ECMAScript 6. Biến được khai báo bằng từ khóa let chỉ có thể được truy cập trong phạm vi của khối được khai báo (phạm vi cục bộ). Khác với var, biến được khai báo bằng let không được hoisted (nâng biến lên đầu phạm vi). Ví dụ:

let myVariable = "Hello World!";

const: là một cách khai báo biến mới khác được giới thiệu trong phiên bản ECMAScript 6. Biến được khai báo bằng từ khóa const cũng có phạm vi cục bộ và giống như let, không được hoisted. Tuy nhiên, biến được khai báo bằng const là biến không thể gán lại giá trị mới. Ví dụ:

const myVariable = "Hello World!";

Lưu ý rằng, khi sử dụng const, chúng ta không thể gán lại giá trị mới cho biến đó, nhưng chúng ta vẫn có thể thay đổi giá trị của thuộc tính trong trường hợp đối tượng hay mảng được gán vào biến đó.

entry

C8:

Kể tên các kiểu dữ liệu cơ bản trong Javascript

Xem đáp án

Kiểu dữ liệu nguyên thủy:

  • Number: Các số nguyên hoặc số thực. Ví dụ: 5 hoặc 5.05
  • String: là các text như “Các kiểu dữ liệu trong JavaScript”, text có thể có một hoặc nhiều ký tự.
  • Boolean: chỉ có 2 giá trị là true hoặc false.
  • Undefine: là các giá trị không xác định.
  • Null: đơn giản là không có giá trị nào cả.
  • Symbol: mới được giới thiệu trong ES6. Nó lưu trữ các giá trị duy nhất và ẩn danh

Kiểu dữ liệu không nguyên thủy (tham chiếu):

  • Object: Thể hiện một đối tượng và các thuộc tính có thể truy cập đến.

  • Array: Nhóm các giá trị giống nhau.

  • RegExp: Biểu thức chính quy.

entry

C9:

Strict mode trong JavaScript là gì?

Xem đáp án

Strict hiểu đơn giản theo nghĩa tiếng Việt là “nghiêm ngặt, nghiêm khắc”. Strict Mode là một quy mẫu nghiêm khắc của Javascript. Nếu như coi việc viết code bình thường là Normal Mode, thì Strict Mode sẽ có thêm nhiều quy định khác so với Normal Mode. Việc đó khiến cho một thao tác vốn bình thường có thể chạy ngon lành trở nên lỗi, và throw ra errors.

Nhìn chung, Strict được tạo ra nhằm:

  • Ngăn chặn sử dụng, và throw errors khi người lập trình thực hiện những xử lý được coi là unsafe, những xử lý mà có thể là ngoài ý muốn.
  • Vô hiệu hoá các tính năng có thể gây nhầm lẫn, hoặc không nên được sử dụng.
  • Ngăn chặn sử dụng một số từ mà có thể sẽ được sử dụng làm keywork trong tương lai. Dưới đây là một số ví dụ

Gán giá trị cho biến chưa được khai báo

"use strict";
variable = "vien.huynh";
console.log(variable);

//Uncaught ReferenceError: variable is not defined

Báo lỗi khi sử dụng delete

"use strict";
function getName(name) {
  alert(name);
}
delete getName;
//Uncaught SyntaxError: Delete of an unqualified
//identifier in strict mode.

Các tham số của hàm không được trùng nhau

"use strict";
function getName(name, name, age) {
  //code
}
//Uncaught SyntaxError: Duplicate parameter name not allowed in this context

Không cho phép khai báo biến dưới dạng hệ nhị phân

var num = 01010;
//Uncaught SyntaxError: Octal literals are not allowed in strict mode.

Không được phép ghi đè lên thuộc tính chỉ được phép đọc

"use strict";
var obj = {};
Object.defineProperty(obj, "ver", { value: 1, writable: false });
obj.ver = 10;

Không sử dụng được with

"use strict";
var bar = 1;
var foo = 2;
with (bar) {
  console.log(foo);
}
//Uncaught SyntaxError: Strict mode code may not include a with statement

Không cho phép khai báo biến trong eval

"use strict";
eval("var x = 4");
alert(x);
//Uncaught ReferenceError: x is not defined

Không chấp nhận khai báo các keyword. Ở chế độ strict mode thì các bạn sẽ không sử dụng được các từ khóa sau để khai báo làm tên biến, hằng,…

  • implements

  • interface

  • let

  • package

  • private

  • protected

  • public

  • static

  • yield

  • arguments

entry

C10:

JavaScript là ngôn ngữ kiểu tĩnh hay kiểu động?

Xem đáp án

JavaScript là một ngôn ngữ kiểu động (dynamic typing language). Điều này có nghĩa là các biến trong JavaScript không được khai báo với một kiểu dữ liệu cụ thể và có thể thay đổi kiểu dữ liệu trong quá trình thực thi chương trình. Ví dụ, một biến có thể được khai báo với kiểu dữ liệu số nguyên (integer) và sau đó được gán giá trị là một chuỗi (string). Các ngôn ngữ kiểu tĩnh (static typing language) như C++ hoặc Java, đòi hỏi các biến phải được khai báo với kiểu dữ liệu cụ thể và giữ nguyên kiểu dữ liệu đó trong suốt quá trình thực thi chương trình.

Ví dụ, trong JavaScript, bạn có thể khai báo một biến mà không cần chỉ định kiểu dữ liệu của nó, như sau:

var myVariable;

Trong ví dụ trên, myVariable là một biến kiểu động, và kiểu dữ liệu của nó sẽ được xác định tại thời điểm thực thi, dựa trên giá trị được gán cho biến. Ví dụ:

myVariable = "Hello World"; // kiểu dữ liệu của myVariable là string
myVariable = 42; // kiểu dữ liệu của myVariable là number

Như vậy, kiểu dữ liệu của biến myVariable đã được xác định tại thời điểm thực thi, và nó có thể thay đổi trong quá trình chạy chương trình.

entry

C11:

Array trong Javascript là gì?

Xem đáp án

Trong Javascript, Array là một kiểu dữ liệu được sử dụng để lưu trữ một tập hợp các giá trị trong một biến. Các giá trị này có thể là bất kỳ kiểu dữ liệu nào, bao gồm cả số, chuỗi, đối tượng và các mảng khác.

Để khởi tạo một mảng mới, ta sử dụng cú pháp:

let myArray = [1, 2, 3, 4, 5];

Ở đây, myArray là tên biến, và danh sách giá trị nằm trong cặp dấu ngoặc vuông [].

Các phần tử của mảng được truy cập thông qua chỉ số (index), bắt đầu từ 0. Ví dụ, để truy cập phần tử thứ hai của mảng myArray, ta sử dụng cú pháp:

Một số phương thức thông dụng của Array trong Javascript bao gồm:

  • push(): Thêm một hoặc nhiều phần tử vào cuối mảng.
  • pop(): Xóa phần tử cuối cùng của mảng.
  • shift(): Xóa phần tử đầu tiên của mảng.
  • unshift(): Thêm một hoặc nhiều phần tử vào đầu mảng.
  • slice(): Trích xuất một phần của mảng.
  • concat(): Kết hợp hai hoặc nhiều mảng lại với nhau để tạo ra một mảng mới.

Ví dụ, để thêm một phần tử mới vào cuối mảng myArray, ta sử dụng phương thức push() như sau:

myArray.push(6);
console.log(myArray); // Output: [1, 2, 3, 4, 5, 6]

entry

C12:

Sự khác nhau giữa nullundefined trong Javascript?

Xem đáp án

Trong JavaScript, nullundefined là hai giá trị đặc biệt đại diện cho sự vắng mặt của giá trị.

Undefined có nghĩa là không xác định. Trong javascript, khi bạn khai báo một biến nhưng chưa gán giá trị cho nó, giá trị của biến đó sẽ là undefined.

Ví dụ:

let x;
console.log(x); // undefined
let obj = {a: 1};
console.log(obj.b); // undefined

Bất cứ biến nào cũng có thể bị làm rỗng bằng cách thiết lập giá trị về không xác định (undefined).

var test = undefined;
alert(test); //undefined

Null có nghĩa là giá trị rỗng hoặc giá trị không tồn tại, nó có thể được sử dụng để gán cho một biến như là một đại diện không có giá trị.

let y = null;
console.log(y); // null

Ngoài ra thì còn một chú ý nữa đó là undefine có kiểu giá trị là undefined nhưng null lại là 1 object

  typeof undefined; // undefined
  typeof null; // object

Vì vậy, khi muốn kiểm tra xem một biến đã được khởi tạo hay chưa, bạn nên sử dụng undefined, trong khi khi muốn chỉ định rõ ràng rằng một giá trị không có ý nghĩa thì nên sử dụng null.

entry

C13:

Giải thích về ép kiểu ngầm trong JavaScript?

Xem đáp án

Ép kiểu ngầm trong javascript là sự chuyển đổi tự động của giá trị từ kiểu dữ liệu này sang kiểu khác. Nó xảy ra khi thực hiện một biểu thức với các kiểu dữ liệu khác nhau.

Ép kiểu String

Ép kiểu string xảy ra khi dùng toán tử +. Một số cộng với một chuỗi, kiểu số sẽ bị ép thành kiểu chuỗi.

Ví dụ:

var x = 3;
var y = "3";
x + y; // Returns "33"
var x = 24;
var y = "Hello";
x + y; // Returns "24Hello";

Để hiểu về hai ví dụ khi ta cộng một số vào chuỗi, thì khi JavaScript thấy biểu thức x+y với hai kiểu khác nhau (một số và một chuỗi), nó chuyển đổi kiểu số thành chuỗi để thực hiện hành động. Sau khi chuyển đổi, cả hai biến đều là kiểu chuỗi, thao tác + lúc này sẽ thành phép nối chuỗi kết quả là ra chuỗi “33” và “24Hello”.

Ngược lại, khi thực hiện phép toán -, thì chuỗi lại bị ép kiểu ngầm thành số. Ví dụ:

var x = 3;
Var y = "3";
x - y    //Returns 0 since the variable y (string type) is converted to a number type

Ép kiểu Boolean

  • Ép kiểu boolean xảy ra khi sử dụng các toán tử logic, lệnh if hay kiểm tra vòng lặp. Để hiểu về ép kiểu logic, ta cần hiểu về giá trị truthyfalsy.
  • Giá trị truthy là cái sẽ được ép kiểu thành true. Còn falsy sẽ được ép kiểu thành false.
  • Tất cả các giá trị ngoại trừ 0, 0n, -0, "", null, undefined, và NaN thì đều là truthy.

Câu lệnh If:

var x = 0;
var y = 23;

if (x) {
  console.log(x);
} // The code inside this block will not run since the value of x is 0(Falsy)

if (y) {
  console.log(y);
} // The code inside this block will run since the value of y is 23 (Truthy)

Toán tử Logic:

  • Toán tử logic trong javascript không giống các ngôn ngữ lập trình khác, nó không trả về true hay false, mà nó trả về một toán hạng.
  • OR ( || ) - Nếu giá trị đầu tiên là truthy, giá trị đầu tiên sẽ được trả về, ngược lại thì nó trả về giá trị thứ hai.
  • AND ( && ) - Nếu hai giá trị đều là truthy, giá trị thứ hai sẽ được trả về. Nếu giá trị đầu là falsy sẽ trả về giá trị đầu hoặc giá trị hai là falsy sẽ trả về giá trị hai.

Ví dụ:

  var x = 220;
  var y = "Hello";
  var z = undefined;

  x | | y    // Returns 220 since the first value is truthy

  x | | z   // Returns 220 since the first value is truthy

  x && y    // Returns "Hello" since both the values are truthy

  y && z   // Returns undefined since the second value is falsy

  if( x && y ){
    console.log("Code runs" ); // This block runs because x && y returns "Hello" (Truthy)
  }

  if( x || z ){
    console.log("Code runs");  // This block runs because x || y returns 220(Truthy)
  }

Ép kiểu dấu bằng

Xảy ra khi thực hiện phép ”==“. Nhớ lại thì phép ”==” được dùng để so sánh hai giá trị khác kiểu.

Thực tế khi sử dụng ”==” một ép kiểu ngầm đã xảy ra, chuyển đổi tất cả toán hạng về cùng kiểu và so sánh chúng.

Ví dụ:

var a = 12;
var b = "12";
a == b; // Returns true because both 'a' and 'b' are converted to the same type and then compared. Hence the operands are equal.

Ép kiểu ngầm không xảy ra khi dùng ”===“.

var a = 226;
var b = "226";

a === b // Returns false because coercion does not take place and the  operands are of different types. Hence they are not equal.

entry

C14:

Toán tử typeof trong Javascript là gì?

Xem đáp án

Toán tử typeof trong Javascript được sử dụng để trả về kiểu dữ liệu của một biến hoặc một giá trị. Toán tử này trả về một chuỗi biểu thị kiểu dữ liệu của biến hoặc giá trị được xác định.

Cú pháp: typeof operand

Ví dụ:

var a;
typeof a; // "undefined"

a = "hello world";
typeof a; // "string"

a = 42;
typeof a; // "number"

a = true;
typeof a; // "boolean"

a = null;
typeof a; // "object" -- weird, bug

a = undefined;
typeof a; // "undefined"

a = { b: "c" };
typeof a; // "object"

entry

C15:

Lập trình bất đồng bộ trong Javascript là gì?

Xem đáp án

Lập trình bất đồng bộ (async) là 1 phần quan trọng trong javascript. Cách tiếp cận phổ biến là sử dụng các callback.

Ví dụ như 1 lệnh ajax gửi request lên server và sau khi nhận được thành công data trả về từ server thì callback sẽ được thực hiện. Thời điểm mà callback được thực hiện không phải là ngay lập tức sau khi có request ajax mà có thể là 1 vài giây sau đó tùy thuộc vào tốc độ xử lí của server.

Ví dụ khác:

console.log("A");
console.log("B");
console.log("C");

thực hiện đoạn code trên cho ra kết quả:

A;
B;
C;

Nhìn vào đoạn code này ta thấy nó hoạt động giống như cơ chế đồng bộ nghĩa là sẽ thực thi từng dòng lệnh một. Ta thay đổi đoạn code trên 1 chút như sau:

console.log("A");

setTimeout(function () {
  console.log("B");
}, 2000);

console.log("C");

Đoạn code trên cho ra kết quả:

A;
C;
B;

Ta thấy rằng thay vì chờ đợi phần lệnh console.log('B') trong setTimeout() chạy xong thì lệnh console.log('C') mới thực hiện giống như cơ chế chạy đồng bộ thì lệnh console.log('C') lại trả về kết quả trước. Đây chính là điểm khác nhau giữa cơ chế bất đồng bộ và đồng bộ trong việc lập trình javascript.

junior

C16:

Kết quả đoạn code sau là gì?

  const person = { name: "Lydia" };
  Object.defineProperty(person, "age", { value: 21 });
  console.log(person);
  console.log(Object.keys(person));
  • A: { name: "Lydia", age: 21 }, ["name", "age"]
  • B: { name: "Lydia", age: 21 }, ["name"]
  • C: { name: "Lydia"}, ["name", "age"]
  • D: { name: "Lydia"}, ["age"]
Xem đáp án

Đáp án: B

Với phương thức defineProperty, chúng ta có thể thêm các thuộc tính mới, cũng như sửa các thuộc tính sẵn có của object. Khi chúng ta thêm thuộc tính vào object bằng defineProperty, chúng sẽ mặc định là thuộc tính not enumerable. Phương thức Object.keys sẽ trả về tất cả các thuộc tính enumerable của object, trong trường hợp này thì chỉ có "name" mà thôi.

Thêm nữa, các thuộc tính được thêm bởi defineProperty là mặc định không thể thay đổi được. Tất nhiên ta có thể override các điều đó bằng các thuộc tính như writable, configurableenumerable. Tức là defineProperty là một cách rất mềm dẻo để tạo ra và điều chỉnh thuộc tính của object.

junior

C17:

Javascript là ngôn ngữ pass-by-reference hay pass-by-value không?

Xem đáp án

Nó luôn pass-by-value, nhưng đối với các đối tượng, giá trị của biến đối tượng đó là một tham chiếu (reference). Do đó, khi bạn truyền một đối tượng và thay đổi các thành viên của nó, những thay đổi đó vẫn tồn tại bên ngoài hàm. Điều này làm cho nó giống như đi qua tham chiếu. Nhưng nếu bạn thực sự thay đổi giá trị của biến đối tượng, bạn sẽ thấy rằng thay đổi không tồn tại, chứng tỏ rằng nó thực sự chuyển qua giá trị. Ví dụ:

function changeStuff(a, b, c) {
  a = a * 10;
  b.item = "changed";
  c = { item: "changed" };
}
var num = 10;
var obj1 = { item: "unchanged" };
var obj2 = { item: "unchanged" };

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);
console.log(obj2.item);

Kết quả:

10;
changed;
unchanged;

junior

C18:

Hàm anonymous là gì và khi nào nên sử dụng?

Xem đáp án

Anonymous Function là gì?

Một Anonymous Function là một hàm không có tên (hay còn gọi là hàm ẩn danh), là một hàm được sinh ra đúng vào thời điểm chạy của chương trình.

Thông thường khi bạn khai báo một hàm thì trình biên dịch sẽ lưu lại trong bộ nhớ nên bạn có thể gọi ở trên hay dưới vị trí khai báo hàm đều được, nhưng với anonymous functions thì nó sẽ được sinh ra khi trình biên dịch xử lý tới vị trí của nó. Ví dụ:

// gọi trước hàm
showDomain(); // hoạt động

function showDomain() {
  alert("Học Javascript tại kungfutech.edu.vn");
}

// gọi sau hàm
showDomain(); // hoạt động

Trong ví dụ này cho dù bạn gọi hàm ở phía trên hay dưới đều hoạt động tốt là vì chương trình đã lưu hàm đó vào bộ nhớ. Nhưng nếu ta sử dụng anonymous function như ví dụ dưới đây sẽ bị lỗi ngay.

// gọi trước hàm
showDomain(); // Lỗi vì hàm này chưa tồn tại

var showDomain = function () {
  alert("Học Javascript tại kungfutech.edu.vn");
};

// gọi sau hàm
showDomain(); // hoạt động vì hàm đã tồn tại

Khi nào thì cần dùng Anonymous Function?

Nếu hàm cần được truyền ở nhiều nơi:

  • Định nghĩa 1 hàm thông thường.
  • Truyền hàm đó vào 1 hàm.

Nếu hàm chỉ truyền 1 nơi?

  • Bất tiện khi tạo ra 1 hàm mới (các chi phí như đặt tên hàm).

  • Giúp tăng tính ràng buộc cho việc chỉ được phép truyền 1 lần.

  • Bên cạnh đó hỗ trợ được thêm khả năng chỉ gọi 1 lần.

junior

C19:

Kết quả đoạn code sau là gì?

  const shape = {
    radius: 10,
    diameter() {
      return this.radius * 2;
    },
    perimeter: () => 2 * Math.PI * this.radius,
  };
  shape.diameter();
  shape.perimeter();
  • A: 20 and 62.83185307179586
  • B: 20 and NaN
  • C: 20 and 63
  • D: NaN and 63
Xem đáp án

Đáp án: B

Chú ý rằng giá trị diameter là một hàm thông thường, còn perimeter là một arrow function.

Không giống như hàm thông thường, với arrow function, biếnthis sẽ trỏ tới surrounding scope! Có nghĩa là khi chúng ta gọi perimeter, nó sẽ không được gọi bởi shape object, mà nó được gọi bởi object nào đó tại surrounding scope (ví dụ window chẳng hạn).

Khi không có giá trị radius tại object đó, nó sẽ trả về undefined.

junior

C20:

Kết quả đoạn code sau là gì?

  const { name: myName } = { name: "Lydia" };
  console.log(name);
  • A: "Lydia"
  • B: "myName"
  • C: undefined
  • D: ReferenceError
Xem đáp án

Đáp án: D

Khi ta tiến hành unpack giá trị name từ object ở phía bên phải, ta đã gán giá trị "Lydia" của nó cho biến có tên là myName.

Với cú pháp { name: myName }, chúng ta muốn khai báo một biến myName với giá trị là giá trị của thuộc tính name trong object phía bên phải.

Do name chưa được định nghĩa, nên ghi log ra, nó sẽ throw ra một ReferenceError.

junior

C21:

Kết quả đoạn code sau là gì?

  let greeting;
  greetign = {}; // Lỗi đánh máy!
  console.log(greetign);
  • A: {}
  • B: ReferenceError: greetign is not defined
  • C: undefined
Xem đáp án

Đáp án: A

Nó sẽ log ra object greetign, bởi vì chúng ta vừa khởi tạo một global object! Khi chúng ta đánh máy nhầm greeting thành greetign, trình thông dịch của JS sẽ coi nó như là global.greetign = {} (hay window.greetign = {} nếu chạy trên browser).

Để tránh điều này chúng ta có thể sử dụng "use strict". Nó sẽ đảm bảo rẳng các biến đều phải được khai báo trước khi sử dụng.

junior

C22:

Viết một hàm có tên lucky_sevens nhận một mảng các số nguyên và trả về giá trị true nếu ba phần tử liên tiếp bất kỳ có tổng bằng 7.

Xem đáp án

Giải pháp:

function lucky_sevens(arr) {
  // nếu mảng có ít hơn 3 phần tử thì trả về false
  if (arr.length < 3) {
    return "not possible";
  }

  // chạy một vòng lặp từ phần tử vị trí 3 đến cuối và kiểm tra xem 3 số liên tiếp có tổng bằng 7 hay không
  for (var i = 2; i < arr.length; i++) {
    if (arr[i] + arr[i - 1] + arr[i - 2] === 7) {
      return true;
    }
  }
  return false;
}

lucky_sevens([2, 1, 5, 1, 0]);

junior

C23:

Đây có phải là một pure function không?

  function sum(a, b) {
    return a + b;
  }
  • A: Yes
  • B: No
Xem đáp án

Đáp án: A

Một hàm được gọi là pure function khi nó luôn luôn trả về một giá trị giống nhau, nếu đối số đưa vào là giống nhau.

Hàm sum luôn trả về giá trị giống nhau. Nếu ta đưa vào 12, nó sẽ luôn trả về 3. Nếu ta đưa vào 510, nó luôn trả về 15. Cứ như vậy, đây là một pure function.

junior

C24:

Điểm khác nhau giữa method GET và method POST?

Xem đáp án

Điểm khác nhau giữa method GET và method POST

GET:

  • Có thể bị cache.
  • Nằm trong history của browser.
  • Có thể bookmarked
  • Dữ liệu truyền đi sẽ hiện trên url và chúng bị giới hạn (độ dài tối đa của URL là 2048).
  • Hạn chế về loại dữ liệu: chỉ có ký tự ASCII được cho phép

POST:

  • Dùng để submit data tới servers.

  • Không bị cache

  • Không nằm trong history của browser.

  • Không thể bookmarked

  • Dữ liệu truyền đi nằm trong body của request.

  • Không bị giới hạn dữ liệu truyền đi.

  • Không giới hạn loại dữ liệu truyền đi.

junior

C25:

Sự khác biệt giữa throw Error('msg') so với throw new Error('msg') là gì?

Xem đáp án

Trong JavaScript, throw Error('msg')throw new Error('msg') là hai cách khác nhau để ném một lỗi (throw an error).

Cú pháp throw Error('msg') sẽ tạo ra một đối tượng lỗi (error object) với thuộc tính message được thiết lập là ‘msg’ và sau đó ném đối tượng lỗi đó. Tuy nhiên, việc này sẽ không cung cấp đầy đủ thông tin về lỗi bởi vì nó không phải là một đối tượng lỗi chuẩn được định nghĩa trong JavaScript.

Còn cú pháp throw new Error('msg') sẽ tạo ra một đối tượng lỗi mới được tạo bởi từ khóa new và thuộc tính message được thiết lập là ‘msg’. Đối tượng lỗi này sẽ được định nghĩa là một đối tượng lỗi chuẩn trong JavaScript, vì vậy nó cung cấp đầy đủ các thuộc tính và phương thức để giúp xác định và xử lý lỗi.

Vì vậy, khi ném lỗi trong JavaScript, thường nên sử dụng cú pháp throw new Error('msg') để đảm bảo rằng đối tượng lỗi được tạo ra là đối tượng lỗi chuẩn và cung cấp đầy đủ các thông tin về lỗi cần thiết để xác định và xử lý lỗi.

junior

C26:

Kết quả đoạn code sau là gì?

  function sayHi() {
    console.log(name);
    console.log(age);
    var name = "Lydia";
    let age = 21;
  }

  sayHi();
  • A: Lydiaundefined
  • B: LydiaReferenceError
  • C: ReferenceError21
  • D: undefinedReferenceError
Xem đáp án

Đáp án: D

Trong hàm chúng ta đã khai báo biến name với var. Điều đó có nghĩa là biến này sẽ được hoisted (một vùng nhớ sẽ được set up khi biến được khởi tạo) với giá trị mặc định là undefined, cho tới khi chúng ta thực sự định nghĩa biến đó. Trong hàm này, chúng ta chưa hề định nghĩa biến name tại dòng mà ta log ra, vậy nên giá trị mặc định của nó vẫn là undefined.

Các biến được khai báo với keyword let (và const) cũng được hoisted nhưng không giống như var, chúng không được khởi tạo. Chúng ta sẽ không thể truy cập chúng cho tới khi chúng ta khai báo (khởi tạo) chúng. Người ta gọi đó là “temporal dead zone”. Khi ta truy cập đến một giá trị trước khi chúng được khai báo, JavaScript sẽ throws một ReferenceError.

junior

C27:

Kết quả đoạn code sau là gì?

  console.log(String.raw`Hello
world`);
  • A: Hello world!
  • B: Hello
         world
  • C: Hello world
  • D: `Hello
Xem đáp án

Đáp án: C

String.raw trả về chuỗi nguyên bản, các ký tự ( , , etc.) sẽ vẫn là nguyên bản và không biến thành xuống dòng hay khoảng trắng! Nếu ta không để là chuỗi nguyên bản, sẽ có trường hợp xảy ra lỗi không mong muốn, ví dụ với đường dẫn:

const path = `C:DocumentsProjects able.html`

Sẽ cho ta chuỗi là:

"C:DocumentsProjects able.html"

Với String.raw, nó sẽ trả về là:

C:DocumentsProjects able.html

Do đó, trong trường hợp này Hello world sẽ được ghi ra.

junior

C28:

Kết quả đoạn code sau là gì?

  const box = { x: 10, y: 20 };

  Object.freeze(box);

  const shape = box;
  shape.x = 100;

  console.log(shape);
  • A: { x: 100, y: 20 }
  • B: { x: 10, y: 20 }
  • C: { x: 100 }
  • D: ReferenceError
Xem đáp án

Đáp án: B

Object.freeze khiến cho chúng ta không thể thêm vào, xóa đi hay thay đổi bất kì thuộc tính nào của object (trừ phi giá trị của thuộc tính lại chính là một object khác).

Khi chúng ta tạo ra biến shape và set cho nó giá trị bằng với một object đã được đóng băng là box, thì shape cũng sẽ trỏ tới một object đã được đóng băng. Ta có thể check một object có đang bị đóng băng hay không bằng Object.isFrozen. Trong trường hợp này, Object.isFrozen(shape) trả về true, vì shape đang trỏ tới một object bị đóng băng.

Do đó, cộng với việc x không phải là object, ta sẽ không thể thay đổi giá trị của x. x sẽ vẫn là 10, và { x: 10, y: 20 } được ghi ra.

junior

C29:

Kết quả đoạn code sau là gì?

  const myPromise = () => Promise.resolve("I have resolved!");
  function firstFunction() {
  myPromise().then((res) => console.log(res));
  console.log("second");
  }
  async function secondFunction() {
  console.log(await myPromise());
  console.log("second");
  }
  firstFunction();
  secondFunction();
  • A: I have resolved!, secondI have resolved!, second
  • B: second, I have resolved!second, I have resolved!
  • C: I have resolved!, secondsecond, I have resolved!
  • D: second, I have resolved!I have resolved!, second
Xem đáp án

Đáp án: D

Có thể tưởng tượng đơn giản cách promise thực thi như sau: bây giờ tôi sẽ để tạm nó sang một bên vì nó tính toán mất thời gian. Chỉ khi nào nó được hoàn thành (resolved) hay bị hủy bỏ (rejected) hay khi call stack trở nên rỗng thì tôi sẽ lấy giá trị trả về ra.

Dù chúng ta có thể sử dụng giá trị thu được bằng cú pháp .then, hoặc sử dụng cặp cú pháp await/async, nhưng, cách chúng hoạt động là khác nhau.

Trong firstFunction, chúng ta đưa promise qua một bên chờ cho nó tính toán xong, và vẫn tiếp tục chạy những code tiếp sau đó, theo đó console.log('second') sẽ được chạy. Sau đó promise được hoàn thành trả về giá trị I have resolved, giá trị này sẽ được log ra khi call stack trở nên rỗng.

Với từ khóa await trong secondFunction, ta đã tạm dừng một hàm bất đồng bộ cho tới khi chúng trả về giá trị, sau đó ta mới đi tiếp đến các câu lệnh tiếp theo.

Do đó nó sẽ chờ cho tới khi myPromise được hoàn thành và trả về giá trị I have resolved, sau đó chúng ta sẽ chạy tiếp câu lệnh tiếp theo in ra second.

junior

C30:

Coercion trong JavaScript là gì?

Xem đáp án

Coercion trong JavaScript là quá trình chuyển đổi tự động của một kiểu dữ liệu sang kiểu dữ liệu khác. Trong JavaScript, có hai loại coercion là implicit coercion (chuyển đổi ngầm định) và explicit coercion (chuyển đổi rõ ràng). Hiểu đơn giản là một cái chuyển kiểu dữ liệu một cách tường minh, mình có thế nhìn thấy được qua mã, trong khi đó kiểu kia thì coercion ngầm định.

Đây là một ví dụ về explicit coercion:

var a = "42";
var b = Number(a);
a; // "42"
b; // 42 -- the number!

Và đây là một ví dụ về implicit coercion:

var a = "42";
var b = a * 1; // "42" implicitly coerced to 42 here
a; // "42"
b; // 42 -- the number!

1. Equality operator (==)

console.log(69 == "69"); // true

Sỡ dĩ điều này xảy ra là gì trước khi phép so sánh thực sự xảy ra, javascript sẽ thực hiện coercion. Nói cách khác, nếu 2 value có cùng type, thì chỉ việc so sánh, nhưng nếu chúng khác type, javascript sẽ cố gắng để convert chúng về cùng 1 type rồi mới so sánh. Ở đây 69 và ‘69’ đã được convert về cùng 1 type là number. 69 vẫn giữ nguyên, nhưng ‘69’ sẽ được convert thành 69. 69 == '69' => 69 == 69 => true coercion không tuần theo 1 logic nào cả, mà nó tuân theo các rules mà ta phải nhớ, áp dụng cho vô số các trường hợp (khi so sánh các value, type với nhau). Tôi sẽ liệt kê ra đây 1 số trường hợp mà các bạn hay gặp

1.1 So sánh number và string

string sẽ được convert thành number, sau đó so sánh. Trường hợp này dẫn tới 2 trường hợp, 1 là string convert được thành number (ví dụ các string như ‘10’, ‘1252’, …), việc so sánh là bình thường. Trường hợp 2 là string không thể convert được thành số (ví dụ các string như ‘abc’, ‘s1fe13324’, …) các gía trị này sẽ convert thành NaN, và như tôi đã nói ở trên, NaN không bằng giá trị nào cả, nên kết quả trả về luôn là false

1.2 So sánh boolean với các type values khác

Đầu tiền boolean value (true => 1, false => 0) thành number, rồi mới so sánh, ví dụ “1” == true sẽ được đổi thành 1 == 1 do true được convert thành number 1 và string “1” convert thành number 1, dẫn tới result là true.

2. Strict equality operator (===)

Operator này sẽ chỉ compare 2 value, just it, không coercion, không convert type.

3. Other operator (+, -, *, /)

Trường hợp mà ta hay gặp nhất với các operator này thao tác với number, number và string. Với toàn number thì không nói làm gì. Nhưng giữa number với string, thì ngoài trừ toán tử + sẽ tiến hành chuyển đổi number thành string rồi tiến hành phép nối string, các toán tử khác (-, *, /) đều sẽ convert string thành number và tiến hành phép toán như thông thường, trong trường hợp không convert được thì các bạn biết điều gì xảy ra rồi đấy (NaN)

4 + "3"; // "43"
4 - "3"; // 1
"4" * "3"; //12
"4" / 3; //1.33333

4 + "a";
("4a");
4 - "a"; // NaN
4 * "a"; // NaN
4 / "a"; //NaN

junior

C31:

Kết quả đoạn code sau là gì?

  async function getData() {
    return await Promise.resolve("I made it!");
  }

  const data = getData();
  console.log(data);
  • A: "I made it!"
  • B: Promise {<resolved>: "I made it!"}
  • C: Promise {<pending>}
  • D: undefined
Xem đáp án

Đáp án: C

Một hàm async luôn luôn trả về một promise. await sẽ chờ cho tới khi promise đó được hoàn thành: một pending promise sẽ được trả về khi ta gọi getData() bằng cách gán nó cho biến data.

Nếu ta muốn truy cập giá trị đã hoàn thành của promise, trong trường hợp này là "I made it", ta có thể sử dụng hàm .then() ngay sau data như sau:

data.then(res => console.log(res))

Khi này nó sẽ ghi ra "I made it!"

junior

C32:

Kết quả đoạn code sau là gì?

  function Car() {
    this.make = "Lamborghini";
    return { make: "Maserati" };
  }

  const myCar = new Car();
  console.log(myCar.make);
  • A: "Lamborghini"
  • B: "Maserati"
  • C: ReferenceError
  • D: TypeError
Xem đáp án

Đáp án: B

Khi chúng ta trả về một thuộc tính, giá trị của thuộc tính bằng với giá trị đã được trả về bởi lệnh return, chứ không phải giá trị được set trong constructor. Chúng ta trả về giá trị là "Maserati", do đó myCar.make sẽ là "Maserati".

junior

C33:

Kết quả đoạn code sau là gì?

  Promise.resolve(5);
  • A: 5
  • B: Promise {<pending>: 5}
  • C: Promise {<fulfilled>: 5}
  • D: Error
Xem đáp án

Đáp án: C

Ta có thể truyền vào giá trị bất kì cho Promise.resolve, dù có là promise hay không promise. Bản thân nó sẽ là một hàm trả về một promise với giá trị đã được resolved.

Trong trường hợp này ta đưa vào giá trị 5. Nó sẽ trả về một resolved promise với giá trị 5.

junior

C34:

Giải thích về phương thức call(), aplly()bind()?

Xem đáp án

call()

Đó là một phương thức được xác định trước trong javascript.

Phương thức này gọi một phương thức (hàm) bằng cách chỉ định đối tượng sở hữu.

Ví dụ 1:

function sayHello() {
  return "Hello " + this.name;
}

var obj = { name: "Sandy" };

sayHello.call(obj);

// Returns "Hello Sandy"

Phương thức call() cho phép một đối tượng sử dụng phương thức của đối tượng khác

Ví dụ 2:

var person = {
  age: 23,
  getAge: function () {
    return this.age;
  },
};

var person2 = { age: 54 };
person.getAge.call(person2);

// Returns 54

call() chấp nhận tham số:

function saySomething(message) {
  return this.name + " is " + message;
}

var person4 = { name: "John" };

saySomething.call(person4, "awesome");
// Returns "John is awesome"

apply()

Tương tự như phương thức call(). Nhưng khác ở điểm phương thức call() nhận các tham số riêng biệt, trong khi apply() nhận tham số là một mảng.

function saySomething(message) {
  return this.name + " is " + message;
}

var person4 = { name: "John" };

saySomething.apply(person4, ["awesome"]);

bind()

Phương thức này trả về một hàm mới, trong đó giá trị của this sẽ được liên kết với đối tượng sở hữu, được cung cấp dưới dạng một tham số.

Ví dụ:

var bikeDetails = {
  displayDetails: function (registrationNumber, brandName) {
    return (
      this.name +
      " , " +
      "bike details: " +
      registrationNumber +
      " , " +
      brandName
    );
  },
};

var person1 = { name: "Vivek" };

var detailsOfPerson1 = bikeDetails.displayDetails.bind(
  person1,
  "TS0122",
  "Bullet",
);

// Binds the displayDetails function to the person1 object

detailsOfPerson1();
// Returns Vivek, bike details: TS0452, Thunderbird

junior

C35:

Giá trị nào của ‘method’ sẽ được trả về với log '{ name: "Lydia", age: 22 }'?

  const keys = ["name", "age"];
  const values = ["Lydia", 22];

  const method =
    /* ?? */
    Object[method](
      keys.map((_, i) => {
        return [keys[i], values[i]];
      }),
    ); // { name: "Lydia", age: 22 }
  • A: entries
  • B: values
  • C: fromEntries
  • D: forEach
Xem đáp án

Đáp án: C

Hàm fromEntries trả về một mảng 2d trong một object. Phần tử đầu tiên trong từng mảng con sẽ là từ khoá và phần tử thứ hai trong từng mảng con sẽ là giá trị. Trong trường hợp này, ta tiến hành map qua mảng keys, nó sẽ trả về một mảng mà phần tử đầu tiên của mảng đó là phần tử trên thứ tự hiện tại của mảng key, và phần tử thứ hai của mảng đó là phần tử trên thứ tự hiện tại của mảng values.

Theo như trên thì ta tạo ra một mảng gồm những mảng con chứa đựng những từ khoá và giá trị đúng, và nó trả về { name: "Lydia", age: 22 }.

junior

C36:

Kết quả đoạn code sau là gì?

  async function getData() {
    return await Promise.resolve("I made it!");
  }

  const data = getData();
  console.log(data);
  • A: "I made it!"
  • B: Promise {<resolved>: "I made it!"}
  • C: Promise {<pending>}
  • D: undefined
Xem đáp án

Đáp án: C

Một hàm async luôn luôn trả về một promise. await sẽ chờ cho tới khi promise đó được hoàn thành: một pending promise sẽ được trả về khi ta gọi getData() bằng cách gán nó cho biến data.

Nếu ta muốn truy cập giá trị đã hoàn thành của promise, trong trường hợp này là "I made it", ta có thể sử dụng hàm .then() ngay sau data như sau:

data.then(res => console.log(res))

Khi này nó sẽ ghi ra "I made it!"

junior

C37:

Hàm setInterval trả về cái gì?

setInterval(() => console.log("Hi"), 1000);
  • A: một id duy nhất
  • B: số lượng milliseconds
  • C: function truyền vào
  • D: undefined
Xem đáp án

Đáp án: A

Nó trả về một id duy nhất. Id này dùng để clear interval sau này với hàm clearInterval().

junior

C38:

Kết quả đoạn code sau là gì?

  let c = { greeting: "Hey!" };
  let d;

  d = c;
  c.greeting = "Hello";
  console.log(d.greeting);
  • A: Hello
  • B: Hey
  • C: undefined
  • D: ReferenceError
  • E: TypeError
Xem đáp án

Đáp án: A

Trong JavaScript, tất cả các object sẽ được tham chiếu khi chúng được gán _bằng_wwwww một giá trị khác.

Đầu tiên, giá trị c có giá trị là một object. Sau đó, chúng ta gán d tham chiếu tới object mà c trỏ tới.

Khi ta thay đổi giá trị của object, tất cả các biến tham chiếu cũng đều thay đổi giá trị theo.

junior

C39:

Kết quả đoạn code sau là gì?

  let a = 3;
  let b = new Number(3);
  let c = 3;

  console.log(a == b);
  console.log(a === b);
  console.log(b === c);
  • A: true false true
  • B: false false true
  • C: true false false
  • D: false true true
Xem đáp án

Đáp án: C

new Number() là một hàm built-in constructor. Mặc dù nó trông có vẻ giống như là một số, nhưng không phải: nó thực sự là một object với hàng tá những thông số khác nữa.

Khi ta sử dụng phép so sánh ==, nó đơn thuần chỉ kiểm tra xem 2 biến có giá trị giống nhau. Chúng đều có giá trị là 3, vậy nên phép toán đầu trả về true.

Tuy nhiên khi sử dụng phép so sánh ===, cả giá trịkiểu đều phải giống nhau. Rõ ràng: new Number() không phải là một số, nó là một object. Cả 2 phép toán sau đều trả về false.

junior

C40:

Kết quả đoạn code sau là gì?

  const add = () => {
    const cache = {};
    return (num) => {
      if (num in cache) {
        return `From cache! ${cache[num]}`;
      } else {
        const result = num + 10;
        cache[num] = result;
        return `Calculated! ${result}`;
      }
    };
  };

 const addFunction = add();
 console.log(addFunction(10));
 console.log(addFunction(10));
 console.log(addFunction(5 * 2));
  • A: Calculated! 20 Calculated! 20 Calculated! 20
  • B: Calculated! 20 From cache! 20 Calculated! 20
  • C: Calculated! 20 From cache! 20 From cache! 20
  • D: Calculated! 20 From cache! 20 Error
Xem đáp án

Đáp án: C

Hàm add chính là một hàm memoized (hàm có nhớ). Với việc có nhớ, chúng ta có thể cache lại kết quả của function để tăng tốc độ tính toán lên. Trong trường hợp này, chúng ta tạo ra một cache object để lưu trữ những kết quả tính toán trước đó.

Mỗi lần chúng ta gọi hàm addFunction với đối số giống nhau, đầu tiên nó sẽ check xem đối số đó có tồn tại trong cache hay không. Nếu có, giá trị trong cache sẽ được trả về luôn, tiết kiệm thời gian tính toán. Còn nếu không thì nó sẽ tiến hành tính toán kết quả và tiếp tục lưu vào cache.

Chúng ta gọi hàm addFunction ba lần với cùng một đối số: trong lần gọi đầu tiên, giá trị của num10 và chưa có mặt trong cache. Do đó num in cache trả về false, và sẽ chạy vào else block: Calculated! 20 sẽ được ghi ra, và 10 sẽ được đưa vào cạche. cache khi này sẽ là { 10: 20 }.

Tại lần gọi thứ hai, cache object đã có giá trị 10. num in cache trả về true, và 'From cache! 20' được ghi ra.

Tại lần gọi thứ ba, ta đưa vào 5 * 2, tức 10 vào hàm. Tiếp tục giống như trên, 'From cache! 20' sẽ được ghi ra.

junior

C41:

Kết quả đoạn code sau là gì?

  for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 1);
  }

  for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 1);
  }
  • A: 0 1 2 and 0 1 2
  • B: 0 1 2 and 3 3 3
  • C: 3 3 3 and 0 1 2
Xem đáp án

Đáp án: C

Bởi vì event queue trong JavaScript, hàm setTimeout callback sẽ được gọi sau khi vòng lặp được thực hiện. Bời vì biến i trong vòng lặp đầu tiên được khai báo với từ khóa var, nên nó sẽ là một biến global. Trong suốt vòng lặp, mỗi lần chúng ta tăng giá trị của i lên 1, sử dụng phép toán ++. Cho tới khi callback setTimeout được gọi, giá trị của i đã trở thành 3 rồi.

Trong vòng lặp thứ 2, biến i được khai báo với từ khóa let, có nghĩa nó là một biến block-scoped (block là những gì được viết bên trong cặp ngoặc { }). Tại mỗi vòng lặp, i sẽ là một biến mới có một giá trị mới, và giá trị đó có scope là bên trong vòng lặp mà thôi.

junior

C42:

Kết quả đoạn code sau là gì?

  +true;
  !"Lydia";
  • A: 1 and false
  • B: false and NaN
  • C: false and false
Xem đáp án

Đáp án: A

Phép toán cộng + sẽ convert một toán hạng sang dạng number. true1, và false is 0.

Chuỗi 'Lydia' là một truthy value. Điều chúng ta thật sự đang hỏi chính là “có phải một giá trị truthy là falsy?“. Rõ ràng câu trả lời là false rồi.

junior

C43:

Cái nào đúng?

  const bird = {
    size: "small",
  };

  const mouse = {
    name: "Mickey",
    small: true,
  };
  • A: mouse.bird.size không hợp lệ
  • B: mouse[bird.size] không hợp lệ
  • C: mouse[bird["size"]] không hợp lệ
  • D: Tất cả đều hợp lệ
Xem đáp án

Đáp án: A

Trong JavaScript thì tất cả keys của các object đều là string (ngoại trừ khi nó là một Symbol). Dù chúng ta không viết chúng như một string, về cơ bản chúng sẽ luôn được chuyển sang dạng string.

JavaScript thông dịch (hay unboxes) từng câu lệnh. Khi chúng ta sử dụng cặp dấu ngoặc [], nó sẽ tìm kiếm dấu mở ngoặc đầu tiên [, và sẽ tiếp tục tìm kiếm cho tới khi gặp dấu đóng ngoặc ]. Chỉ khi đó thì câu lệnh mới được thực thi.

mouse[bird.size]: Giá trị đầu tiên bird.size"small". mouse["small"] sẽ trả về true

Tuy nhiên, khi chúng ta sử dụng dấu chấm ., điều trên không còn đúng nữa. mouse không hề có key nào tên là bird, có nghĩa mouse.bird sẽ là undefined. Sau đó chúng ta gọi size sử dụng chấm .: mouse.bird.size. Vì mouse.birdundefined, lời gọi sẽ trở thành undefined.size. Đây là một lời gọi không hợp lệ, nó sẽ throw ra một lỗi kiểu như Cannot read property "size" of undefined.

junior

C44:

Thuộc tính NaN trong JavaScript là gì?

Xem đáp án

Thuộc tính NaN biểu diễn một giá trị Not-a-Number. Nó biểu thị một giá trị không phải là số.

typeof của NaN trả về Number. Muốn kiểm tra một giá trị có phải NaN không, có thể dùng hàm isNaN().

Ví dụ:

isNaN("Hello"); // Returns true
isNaN(345); // Returns false
isNaN("1"); // Returns false, since '1' is converted to Number type which results in 0 ( a number)
isNaN(true); // Returns false, since true converted to Number type results in 1 ( a number)
isNaN(false); // Returns false
isNaN(undefined); // Returns true

junior

C45:

Cách lặp các phần tử của mảng trong Javascript?

Xem đáp án

Cách thứ nhất: sử dụng for, cạm bẫy phổ biến ở đây là var nằm trong phạm vi hàm chứ không phải phạm vi khối và hầu hết bạn muốn biến i thuộc phạm vi khối. ES2015 giới thiệu let có phạm vi khối và bạn nên sử dụng nó để thay thế. Vì - vậy, điều này trở thành

for (let i = 0; i <arr.length; i ++) {
  ...for (let i = 0; i <arr.length; i ++)
}

Cách thứ 2: sử dụng forEach, cấu trúc này đôi khi có thể thuận tiện hơn vì bạn không phải sử dụng index nếu tất cả những gì bạn cần là các phần tử mảng. Ngoài ra còn có các phương thức everysome sẽ cho phép bạn kết thúc - sớm quá trình lặp.

arr.forEach(function (el, index) {
  ...
}).

Tuy vậy, vòng lặp for cho phép linh hoạt hơn, chẳng hạn như kết thúc sớm vòng lặp bằng cách sử dụng break hoặc tăng vòng lặp nhiều hơn.

junior

C46:

Kết quả đoạn code sau là gì?

  function addToList(item, list) {
    return list.push(item);
  }

  const result = addToList("apple", ["banana"]);
  console.log(result);
  • A: ['apple', 'banana']
  • B: 2
  • C: true
  • D: undefined
Xem đáp án

Đáp án: B

Hàm .push() trả về độ dài của mảng mới! Trước đó, mảng chỉ hồm một phần tử là "banana" và có độ dài là 1. Sau khi thêm chuỗi "apple" vào mảng, mảng lúc này có hai chuỗi và có độ dài là 2. Do đó hàm addToList sẽ trả về 2.

Hàm push sẽ thay đổi chính bản thân mảng truyền vào. Do đó nếu chúng ta muốn trả về mảng thay vì chỉ trả về độ dài, chúng ta nên trả về trực tiếp mảng list sau khi đã thêm item vào đó.

junior

C47:

Làm sao để clone một mảng?

Xem đáp án

Để clone một mảng, chúng ta có thể sử dụng một trong các cách dưới đây:

1. Sử dụng hàm slice

  • Hàm slice() là một phương thức của mảng trong Javascript và được sử dụng để sao chép một phần hoặc toàn bộ các phần tử của mảng.
  • Khi sử dụng slice() để clone một mảng, chúng ta sẽ tạo ra một bản sao của mảng gốc. Các thay đổi trên mảng sao chép sẽ không ảnh hưởng đến mảng gốc, và ngược lại.

Ví dụ:

const originalArray = [1, 2, 3, 4];
const clonedArray = originalArray.slice();
Khi nào nên sử dụng? Khi bạn cần clone một mảng và không muốn thay đổi mảng gốc hoặc mảng sao chép.

2. Sử dụng hàm JSON.stringify()JSON.parse() (deep copy)

  • Hàm JSON.stringify() được sử dụng để chuyển đổi một đối tượng Javascript thành một chuỗi JSON.
  • Hàm JSON.parse() được sử dụng để chuyển đổi một chuỗi JSON thành một đối tượng Javascript.
  • Khi sử dụng JSON.stringify()JSON.parse() để clone một mảng, chúng ta sẽ tạo ra một bản sao sâu của mảng gốc. Các thay đổi trên mảng sao chép sẽ không ảnh hưởng đến mảng gốc, và ngược lại.

Ví dụ:

const originalArray = [1, 2, 3, 4];
const clonedArray = originalArray.slice();
Khi nào nên sử dụng? Khi bạn cần sao chép một đối tượng hoặc mảng sâu (deep copy)

3. Sử dụng toán tử spread operator [...] trong ES6:

  • Toán tử spread (...) là một tính năng mới được giới thiệu trong ES6, cho phép chúng ta truyền các phần tử của một mảng vào trong một mảng khác.
  • Khi sử dụng toán tử spread operator để clone một mảng, chúng ta sẽ tạo ra một bản sao của mảng gốc. Các thay đổi trên mảng sao chép sẽ không ảnh hưởng đến mảng gốc, và ngược lại.

Ví dụ:

const originalArray = [1, 2, 3, 4];
const clonedArray = [...originalArray];
Khi nào nên sử dụng? Khi bạn đang sử dụng ES6 và muốn clone một mảng.

4. Sử dụng Object.assign

  • Hàm Object.assign() được sử dụng để sao chép giá trị của tất cả các thuộc tính có thể liệt kê từ một hoặc nhiều đối tượng nguồn đến một đối tượng đích. Nó trả về đối tượng đích.

Ví dụ:

let originArr = [1, 2, 3];
let newArr = Object.assign([], originArr);
console.log(newArr); // Output: [1, 2, 3]
Khi nào nên sử dụng? Khi muốn sao chép các giá trị của các thuộc tính từ một hoặc nhiều đối tượng nguồn đến một đối tượng đích hoặc khi muốn sử dụng đối tượng đích để thêm hoặc cập nhật các thuộc tính mà không làm thay đổi các đối tượng nguồn.
Tuy nhiên, hạn chế của phương pháp này là không thể sao chép các thuộc tính không liệt kê được (như các thuộc tính kế thừa). Ngoài ra, nếu các đối tượng nguồn có các thuộc tính có cùng tên với nhau, giá trị của thuộc tính cuối cùng sẽ được sao chép.

5. Sử dụng Array.map()

  • Phương thức Array.map() cũng có thể được sử dụng để clone một mảng. Đây là một phương pháp khá đơn giản, bạn chỉ cần tạo một mảng mới bằng cách ánh xạ các giá trị của mảng gốc sang mảng mới. Ví dụ:
const arrOrigin = [1, 2, 3];
const newArr = arrOrigin.map(x => x);

Trong đó, arrOrigin.map() sẽ tạo ra một mảng mới bằng cách ánh xạ các giá trị của mảng gốc arrOrigin vào mảng mới. Mảng mới này sẽ được gán cho biến newArr.

Tuy nhiên, cách này không được khuyến khích bởi vì nó tốn nhiều tài nguyên và không hiệu quả so với các cách khác để sao chép một mảng.

Ngoài ra chúng ta cũng có thể clone mảng qua các hàm như vòng lặp while(), vòng lặp for(), Array.filter(),…

junior

C48:

Điều gì sẽ xảy ra khi chúng ta làm thế này?

  function bark() {
    console.log("Woof!");
  }

  bark.animal = "dog";
  • A: Hoàn toàn không có vấn đề gì!
  • B: SyntaxError. Bạn không thể thêm thuộc tính theo cách này.
  • C: undefined
  • D: ReferenceError
Xem đáp án

Đáp án: A

Điều này là có thể với Javascript, bởi vì function cũng chỉ là object mà thôi! (Mọi primitive types đều là object)

Function là một object đặc biệt. Phần code mà bạn viết không phải là function thực tế đâu. Function ở đây chính là một object với các thuộc tính. Và các thuộc tính này có thể gọi được.

junior

C49:

Kết quả đoạn code sau là gì?

  function addToList(item, list) {
    return list.push(item);
  }

  const result = addToList("apple", ["banana"]);
  console.log(result);
  • A: ['apple', 'banana']
  • B: 2
  • C: true
  • D: undefined
Xem đáp án

Đáp án: B

Hàm .push() trả về độ dài của mảng mới! Trước đó, mảng chỉ hồm một phần tử là "banana" và có độ dài là 1. Sau khi thêm chuỗi "apple" vào mảng, mảng lúc này có hai chuỗi và có độ dài là 2. Do đó hàm addToList sẽ trả về 2.

Hàm push sẽ thay đổi chính bản thân mảng truyền vào. Do đó nếu chúng ta muốn trả về mảng thay vì chỉ trả về độ dài, chúng ta nên trả về trực tiếp mảng list sau khi đã thêm item vào đó.

junior

C50:

Cách để lặp qua các thuộc tính đối tượng trong Javascript?

Xem đáp án

Cách thứ nhất: sử dụng for...in, tuy nhiên, điều này cũng sẽ lặp lại qua các thuộc tính kế thừa của nó và chúng ta có thể thêm một lệnh kiểm tra obj.hasOwnProperty(property) trước khi sử dụng nó.

for (var property in obj) {
  console.log(property);
}

Cách thứ hai: sử dụng Object.keys() là một phương thức tĩnh sẽ liệt kê tất cả các thuộc tính có thể liệt kê của đối tượng mà bạn truyền nó vào.

Object.keys(obj).forEach(function (property) {
  ...
})

Cách thứ ba: sử dụng Object.getOwnPropertyNames() là một phương thức tĩnh sẽ liệt kê tất cả các thuộc tính có thể liệt kê và không thể liệt kê của đối tượng mà bạn truyền nó.

Object.getOwnPropertyNames(obj).forEach(function (property) {
  ...
})

Cách thứ tư: sử dụng Object.entries() để tạo một mảng với tất cả các thuộc tính có thể liệt kê của nó và lặp qua.

Object.entries(items).map((item) => {
  ...
});
Object.entries(items).forEach((item) => {
  ...
});
for (const item of Object.entries(items)) {
  ...
}

junior

C51:

Kết quả đoạn code sau là gì?

  console.log("❤️" === "❤️");
  • A: true
  • B: false
Xem đáp án

Đáp án: A

Về cơ bản, emoji vẫn là các ký tự unicode mà thôi. Mã unicode cho hình trái tim là "U+2764 U+FE0F". Chúng luôn luôn là một, nên phép toán đơn giản trả về true.

junior

C52:

Kết quả đoạn code sau là gì?

  class Chameleon {
    static colorChange(newColor) {
      this.newColor = newColor;
      return this.newColor;
    }

    constructor({ newColor = "green" } = {}) {
      this.newColor = newColor;
    }
  }

  const freddie = new Chameleon({ newColor: "purple" });
  freddie.colorChange("orange");
  • A: orange
  • B: purple
  • C: green
  • D: TypeError
Xem đáp án

Đáp án: D

Hàm colorChange là một hàm static (hàm tĩnh). Hàm static được thiết kế để chỉ để tồn tại ở mức class, và không thể truyền cho bất cứ instance con nào. Vì freddie là một instance con, hàm static này sẽ không được truyền xuống, và do đó không thể gọi được tại freddie instance: nó sẽ throw ra một TypeError.

junior

C53:

Giải thích về phép gán quá giá trị và phép gán qua tham chiếu?

Xem đáp án

Trong JavaScript, kiểu dữ liệu nguyên thuỷ được gán với giá trị, còn kiểu đối tượng được gán bằng tham chiếu.

Trước tiên, ta cần hiểu về điều gì xảy ra khi ta tạo một biến và gán giá trị cho nó.

var x = 2;

Trong ví dụ trên, ta tạo một biến x và gán nó giá trị là “2”. Phép ”=” chỉ định một vài không gian trong bộ nhớ, để lưu trữ giá trị là “2” và trả về vị trí được chỉ định trong bộ nhớ. Do đó, biến x ở trên trỏ đến vị trí trong bộ nhớ thay vì trỏ trực tiếp đến giá trị 2.

Phép gán thực hiện hành vi khác nhau khi làm việc với kiểu nguyên thuỷ và kiểu đối tượng.

Phép gán với kiểu nguyên thuỷ

Phép gán với kiểu nguyên thuỷ

var y = 234;
var z = y;

Ở ví dụ này, dòng đầu phép gán giá trị cho y là kiểu nguyên thuỷ, sau đó ở dòng thứ hai, giá trị của y được gán cho z. Phép gán chỉ định một vùng không gian mới trong bộ nhớ và trả về địa chỉ của nó. Do đó, biến z không chỉ đến vị trí của biến y thay vào đó nó chỉ đến vùng không gian mới trong bộ nhớ.

var y = #8454; // y pointing to address of the value 234

var z = y;

var z = #5411; // z pointing to a completely new address of the value 234

// Changing the value of y
y = 23;
console.log(z);  // Returns 234, since z points to a new address in the memory so changes in y will not effect z

Từ ví dụ trên, ta có thể thấy rằng các kiểu dữ liệu nguyên thủy khi được truyền cho một biến khác sẽ được truyền theo giá trị. Thay vì chỉ gán cùng một địa chỉ cho một biến khác, giá trị sẽ được gán và không gian bộ nhớ mới được tạo ra.

Phép gán với kiểu đối tượng

Phép gán với kiểu đối tượng

var obj = { name: "Vivek", surname: "Bisht" };

var obj2 = obj;

Trong ví dụ trên, phép gán truyền trực tiếp vị trí của biến obj đến biến obj2. Nói cách khác, tham chiếu của biến obj được chuyển cho biến obj2.

var obj = #8711;  // obj pointing to address of { name: "Vivek", surname: "Bisht" }

var obj2 = obj;

var obj2 = #8711; // obj2 pointing to the same address

// changing the value of obj1

obj1.name = "Akki";

console.log(obj2);

// Returns {name:"Akki", surname:"Bisht"} since both the variables are pointing to the same address.

Từ ví dụ trên, ta có thể thấy rằng trong khi truyền các kiểu dữ liệu đối tượng, phép gán trực tiếp truyền địa chỉ (tham chiếu).

Do đó, các kiểu dữ liệu đối tượng luôn được truyền bằng tham chiếu.

junior

C54:

Bạn nghĩ gì về AMD (Asynchronous Module Definition) và CommonJS?

Xem đáp án

Mặc dù JavaScript không hỗ trợ các module, nhưng cộng đồng các developer đã cố gắng tìm ra cách để làm việc này. Sau một thời gian phát triển, thì hiện nay có một số phương thức module hóa như sau:

  • The Module pattern
  • CommonJS
  • Asynchronous Module Definition (AMD)

Bạn nghĩ gì về AMD (Asynchronous Module Definition) và CommonJS

Trong những phương án trên, module pattern không yêu cầu bất cứ một công cụ hay thư viện nào, nó có thể hoạt động ở mọi môi trường JavaScript. CommonJS hướng tới mục tiêu là JavaScript chạy ở server-side. AMD chính là phương thức rất phổ biến với những ứng dụng Web, và nó cũng là phương thức mà RequireJS sử dụng.

Cả hai đều là cách để triển khai một module system, vốn không có trong JavaScript cho đến khi ES2015 ra đời. CommonJS là đồng bộ trong khi AMD là bất đồng bộ. CommonJS được thiết kế với sự phát triển phía máy chủ trong khi AMD, hỗ trợ tính năng tải các modules một cách bất đồng bộ, dành cho trình duyệt nhiều hơn.

Cú pháp AMD khá dài dòng và CommonJS gần với kiểu bạn viết câu lệnh import trong các ngôn ngữ khác. Hầu hết thời gian, tôi thấy AMD không cần thiết, bởi vì nếu bạn đưa tất cả JavaScript của mình vào một tệp gói được nối, bạn sẽ không được hưởng lợi từ các thuộc tính tải bất đồng bộ. Ngoài ra, cú pháp CommonJS gần với phong cách viết module của Node hơn và có ít chi phí chuyển đổi ngữ cảnh hơn khi chuyển đổi giữa phát triển JavaScript phía máy khách và phía máy chủ.

Thật vui vì với các modules ES2015, có hỗ trợ cả tải đồng bộ và bất đồng bộ, cuối cùng chúng ta có thể chỉ cần bám vào một cách tiếp cận. Mặc dù nó chưa được triển khai hoàn toàn trong các trình duyệt và trong Node, nhưng chúng ta luôn có thể sử dụng các bộ chuyển mã để chuyển đổi mã của mình.

middle

C55:

Kết quả đoạn code sau là gì?

  const set = new Set([1, 1, 2, 3, 4]);

  console.log(set);
  • A: [1, 1, 2, 3, 4]
  • B: [1, 2, 3, 4]
  • C: {1, 1, 2, 3, 4}
  • D: {1, 2, 3, 4}
Xem đáp án

Đáp án: D

Set là một tập hơp các giá trị không trùng nhau.

Chúng ta đưa đầu vào là một mảng [1, 1, 2, 3, 4] với giá trị 1 bị trùng. Giá trị trùng đó sẽ bị loại bỏ. Kết quả là {1, 2, 3, 4}.

middle

C56:

Anonymous Function thường dùng cho trường hợp nào?

Xem đáp án

Anonymous Function có thể được sử dụng trong IIFE để đóng gói một số mã trong phạm vi cục bộ để các biến được khai báo trong đó không bị rò rỉ ra phạm vi toàn cục.

(function () {
  // Some code here.
})();

Anonymous Function như một callback được sử dụng một lần và không cần sử dụng ở bất kỳ nơi khác. Code sẽ có vẻ tự kiểm soát và dễ đọc hơn khi các trình xử lý được xác định ngay bên trong code gọi chúng, thay vì phải tìm kiếm ở nơi khác để tìm phần thân hàm.

setTimeout(function () {
  console.log("Hello world!");
}, 1000);

Đối số đến cấu trúc functional programming hoặc Lodash (tương tự như callback).

const arr = [1, 2, 3];
const double = arr.map(function (el) {
  return el * 2;
});
console.log(double); // [2, 4, 6]

middle

C57:

Ưu điểm và nhược điểm của việc sử dụng use strict là gì?

Xem đáp án

Answer `use strict` là một câu lệnh được sử dụng để bật strict mode cho toàn bộ tập lệnh hoặc các chức năng riêng lẻ. Strict mode là một cách để chọn tham gia một biến thể bị hạn chế (hạn chế một số tính năng) của JavaScript.

Ưu điểm:

  • Làm cho nó không thể vô tình tạo ra các biến toàn cục.
  • Đẩy ra các exception nếu xóa các thuộc tính không thể bị xóa (trước đó việc xóa đó chỉ đơn giản là không có tác dụng và cũng không có thông báo gì).
  • Bắt buộc tên tham số hàm là duy nhất.
  • thisundefined trong global context.
  • Nó bắt một số blooper mã hóa phổ biến, thảy ra các exceptions.
  • Nó vô hiệu hóa các tính năng khó hiểu hoặc không hiệu quả.

Nhược điểm:

  • Nhiều tính năng bị thiếu mà một số nhà phát triển có thể đã quen dùng.
  • Không còn quyền truy cập vào function.caller và function.arguments.
  • Việc kết hợp các tập lệnh được viết ở các strict mode khác nhau có thể gây ra sự cố.

Nhìn chung, tôi nghĩ rằng “use strict” có lợi ích nhiều hơn bất lợi.

middle

C58:

Lợi ích của việc sử dụng spread trong ES6 so với rest như thế nào?

Xem đáp án

Cú pháp spread của ES6 rất hữu ích khi viết mã theo mô hình functional vì chúng ta có thể dễ dàng tạo bản sao của mảng hoặc đối tượng mà không cần dùng đến Object.create, slice hoặc một hàm thư viện. Tính năng này thường được sử dụng trong các dự án Redux và rx.js.

function putDookieInAnyArray(arr) {
  return [...arr, "dookie"];
}

const result = putDookieInAnyArray(["I", "really", "don't", "like"]);
// ["I", "really", "don't", "like", "dookie"]

const person = {
  name: "Todd",
  age: 29,
};
const copyOfTodd = { ...person };

Cú pháp rest của ES6 cung cấp một cách viết tắt để có thể truyền một số lượng đối số tùy ý vào một hàm. Nó giống như một phép nghịch đảo của cú pháp spread, lấy dữ liệu và nhồi nó vào một mảng chứ không phải giải nén một mảng dữ liệu, và nó hoạt động trong các đối số hàm như trong các array destructuring và object destructuring.

middle

C59:

Giá trị trả về là gì?

  const firstPromise = new Promise((res, rej) => {
    setTimeout(res, 500, "one");
  });

  const secondPromise = new Promise((res, rej) => {
    setTimeout(res, 100, "two");
  });

  Promise.race([firstPromise, secondPromise]).then(res => console.log(res));
  • A: "one"
  • B: "two"
  • C: "two" "one"
  • D: "one" "two"
Xem đáp án

Đáp án: B

Khi chúng ta đưa các promise vào trong một hàm Promise.race, nó sẽ chỉ resolves hay rejects promise đầu tiên được resolves/rejects. Với hàm setTimeout, chúng ta đưa vào một khoảng thời gian: 500 mili giây cho promise đầu tiên (firstPromise), và 100 mili giây cho promise thứ hai (secondPromise). Nó có nghĩa là secondPromise sẽ hoàn thành trước và trả về giá trị 'two'. res khi này sẽ nhận giá trị 'two' và được in ra console.

middle

C60:

Kết quả đoạn code sau là gì?

  function getInfo(member, year) {
    member.name = "Lydia";
    year = "1998";
  }

  const person = { name: "Sarah" };
  const birthYear = "1997";

  getInfo(person, birthYear);

  console.log(person, birthYear);
  • A: { name: "Lydia" }, "1997"
  • B: { name: "Sarah" }, "1998"
  • C: { name: "Lydia" }, "1998"
  • D: { name: "Sarah" }, "1997"
Xem đáp án

Đáp án: A

Đối số sẽ được đưa vào hàm dạng tham trị, trừ phi nó là object, khi đó nó sẽ được đưa vào hàm dạng tham chiếu. birthYear là dạng giá trị, vì nó là string chứ không phải object. Khi chúng ta đưa vào dạng giá trị, một bản sao của giá trị đó sẽ được tạo ra (xem thêm câu 46).

birthYear trỏ đến giá trị là "1997". Đối số year cũng sẽ rỏ đến giá trị "1997", nhưng giá trị này chỉ là một bản sao của giá trị mà birthYear trỏ tới mà thôi, hai giá trị đó hoàn toàn khác nhau. Do đó khi ta thay đổi giá trị year bằng "1998", chúng ta chỉ thay đổi giá trị của year mà thôi. birthYear sẽ vẫn giữ giá trị là "1997".

person là một object. Biến member có một tham chiếu tới cùng object mà person trỏ tới. Khi chúng ta thay đổi một thuộc tính của object mà member trỏ tới, giá trị của person cũng sẽ tự động thay đổi theo, vì chúng có chung tham chiếu. name của person khi này sẽ có giá trị mới là "Lydia".

middle

C61:

Sự khác biệt giữa các Host objectsNative objects là gì?

Xem đáp án

  • Native objects là một phần của ngôn ngữ JavaScript được xác định bởi đặc tả ECMAScript, chẳng hạn như String, Math, RegExp, Object, Function, …

  • Host objects được cung cấp bởi môi trường runtime (trình duyệt hoặc Node), chẳng hạn như window, XMLHTTPRequest,…

middle

C62:

Kết quả đoạn code sau là gì?

  // counter.js
  let counter = 10;
  export default counter;
// index.js
import myCounter from "./counter";

myCounter += 1;

console.log(myCounter);
  • A: 10
  • B: 11
  • C: Error
  • D: NaN
Xem đáp án

Đáp án: C

Một module khi được import sẽ là read-only: chúng ta sẽ không thể chỉnh sửa module đó, chỉ có bản thân module đó có thể chỉnh sửa giá trị của nó mà thôi.

Khi ta thay đổi giá trị cuả myCounter, nó sẽ throw ra một lỗi: myCounterread-only và không thể thay đổi.

middle

C63:

Làm thế nào để so sánh hai object trong JavaScript?

Xem đáp án

Các giá trị non-primitive, như các object (bao gồm cả hàm và mảng) được lưu dưới dạng tham chiếu, vì vậy cả hai phép so sánh ===== sẽ chỉ kiểm tra xem các tham chiếu có khớp nhau hay không, chứ không phải kiểm tra bất kỳ điều gì về các giá trị cơ bản.

Ví dụ: theo mặc định, mảng được ép thành chuỗi bằng cách chỉ cần nối tất cả các giá trị bằng dấu phẩy (,) ở giữa. Vì vậy, hai mảng có cùng nội dung sẽ không là true khi so sánh bằng ==

var a = [1, 2, 3];
var b = [1, 2, 3];
var c = "1,2,3";
a == c; // true
b == c; // true
a == b; // false

Để deep-comparison các object, hãy sử dụng các thư viện bên ngoài như deep-equal hoặc triển khai một thuật toán của riêng bạn. Dưới đây là một ví dụ

function deepObjectEqual(object1, object2) {
  const keys1 = Object.keys(object1);
  const keys2 = Object.keys(object2);

  if (keys1.length !== keys2.length) {
    return false;
  }

  for (const key of keys1) {
    const val1 = object1[key];
    const val2 = object2[key];
    const areObjects = isObject(val1) && isObject(val2);

    if (
      (areObjects && !deepObjectEqual(val1, val2)) ||
      (!areObjects && val1 !== val2)
    ) {
      return false;
    }
  }

  return true;
}

function isObject(object) {
  return object != null && typeof object === "object";
}

Như bạn thấy bên trên, đoạn areObjects && !deepObjectEqual(val1, val2) ta thực hiện đệ quy để có thể lặp hết vào trong các thuộc tính của object. Cùng xem ví dụ thử nhé:

const object1 = {
  value: 1,
  name: "Test",
  address: {
    city: "Ha Noi",
  },
};

const object2 = {
  value: 1,
  name: "Test",
  address: {
    city: "Ha Noi",
  },
};

deepObjectEqual(object1, object2); // => true

So sánh sâu sẽ giúp ta xác định một cách chính xác 2 object có thực sự bằng nhau về mặt nội dung hay không.

middle

C64:

So sánh sự khác nhau của forEach()map()?

Xem đáp án

Để hiểu sự khác biệt giữa hai hàm, chúng ta hãy xem mỗi hàm làm gì.

forEach() trong Javascript

  • Lặp qua các phần tử trong một mảng.
  • Thực hiện một lệnh gọi lại cho mỗi phần tử.
  • Không trả về giá trị.
const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
  // Do something with num and/or index.
});
// doubled = undefined

map() trong Javascript

  • Lặp qua các phần tử trong một mảng.
  • Ánh xạ mỗi phần tử thành một phần tử mới bằng cách gọi hàm trên mỗi phần tử, kết quả là tạo ra một mảng mới.
const a = [1, 2, 3];
const doubled = a.map(num => {
  return num * 2;
});
// doubled = [2, 4, 6]

Sự khác biệt chính giữa forEach()map() là map trả về một mảng mới. Nếu bạn cần kết quả, nhưng không muốn thay đổi mảng ban đầu, map là lựa chọn rõ ràng. Nếu bạn chỉ cần lặp lại một mảng, forEach() là một lựa chọn tốt.

middle

C65:

Kết quả đoạn code sau là gì?

  const settings = {
    username: "lydiahallie",
    level: 19,
    health: 90,
  };

  const data = JSON.stringify(settings, ["level", "health"]);
  console.log(data);
  • A: "{"level":19, "health":90}"
  • B: "{"username": "lydiahallie"}"
  • C: "["level", "health"]"
  • D: "{"username": "lydiahallie", "level":19, "health":90}"
Xem đáp án

Đáp án: A

Đối số thứ hai của JSON.stringifyreplacer. Replacer Có thể là một hàm hoặc một mảng, nó sẽ quy định xem giá trị nào sẽ được chuỗi hóa ra sao.

Nếu replacer là một mảng, chỉ có các thuộc tính có tên trong mảng được convert thành chuỗi JSON. Trong trường hợp này, chỉ có các thuộc tính "level""health" được đưa vào, "username" bị loại bỏ. data giờ sẽ là "{"level":19, "health":90}".

Nếu replacer là function, hàm này sẽ được gọi trên từng thuộc tính của object được chuỗi hóa. Giá trị trả về sẽ là giá trị được đưa vào chuỗi JSON. Nếu trả về undefined, thuộc tính này sẽ bị loại bỏ khỏi chuỗi.

middle

C66:

Kết quả đoạn code sau là gì?

  const one = false || {} || null;
  const two = null || false || "";
  const three = [] || 0 || true;
  console.log(one, two, three);
  • A: false null []
  • B: null "" true
  • C: {} "" []
  • D: null null true
Xem đáp án

Đáp án: C

Với phép toán ||, ta sẽ trả về giá trị truethy đầu tiên. Nếu tất cả đều là falsy, giá trị cuối cùng sẽ được trả về.

(false || {} || null): object rỗng {} là một giá trị truthy. Nó là giá trị truethy đầu tiên và duy nhất nên sẽ được trả về. Do đó one sẽ là {}.

(null || false || ""): Tất cả toán hạng đều là falsy. Có nghĩa là toán hạng cuối cùng "" sẽ được trả về. Do đó two sẽ là "".

([] || 0 || ""): mảng rỗng [] là một giá trị truthy. Nó là giá trị truthy đầu tiên nên sẽ được trả về. Do đó three sẽ là [].

middle

C67:

Bạn biết gì về sự kiện load trong Javascript?

Xem đáp án

Sự kiện onload có ý nghĩa rằng khi trình duyệt đã load xong mọi thứ (image, js, css) thì những đoạn code nằm bên trong đó mới được chạy. Có một lưu ý rằng nếu bạn sử dụng onload cho một thẻ HTML nào đó thì nó sẽ có tác dụng với thẻ HTML đó thôi nhưng nếu bạn dùng cho window thì nó sẽ có tác dụng cho toàn trang.

Hay nói cách khác những đoạn code nằm bên trong sự kiện onload sẽ được chạy sau cùng khi mà trình JS đã được biên dịch 1 lần. Chính vì vậy nếu trong sự kiện onload bạn gọi tới một hàm nào đó thì dù bạn đặt hàm đó phía trên hay phía dưới thì đều đúng, lý do là trình biên dịch đã thực hiện dịch 1 lần rồi nên hàm đó đã tồn tại.

Ví dụ: trong đoạn code dưới đây ta gọi hàm do_validate() bên trong sự kiện window.onload nên mặc dù hàm validate được đặt phía dưới nhưng vẫn đúng.

window.onload = function () {
  do_validate();
};

function do_validate() {
  alert(1);
}

Nếu vẫn chưa tin thì bạn làm ví dụ sau đây, trong ví dụ này ta thực hiện alert lên thứ tự của quá trình biên dịch

alert(1);

window.onload = function () {
  alert(3);
};

alert(2);

Mặc dù đoạn code alert(3) nằm ở vị trí thứ hai nhưng nó lại xuất hiện cuối cùng.

middle

C68:

Kết quả đoạn code sau là gì?

  const box = { x: 10, y: 20 };
  Object.freeze(box);
  const shape = box;
  shape.x = 100;
  console.log(shape);
  • A: { x: 100, y: 20 }
  • B: { x: 10, y: 20 }
  • C: { x: 100 }
  • D: ReferenceError
Xem đáp án

Đáp án: B

Object.freeze khiến cho chúng ta không thể thêm vào, xóa đi hay thay đổi bất kì thuộc tính nào của object (trừ phi giá trị của thuộc tính lại chính là một object khác).

Khi chúng ta tạo ra biến shape và set cho nó giá trị bằng với một object đã được đóng băng là box, thì shape cũng sẽ trỏ tới một object đã được đóng băng. Ta có thể check một object có đang bị đóng băng hay không bằng Object.isFrozen. Trong trường hợp này, Object.isFrozen(shape) trả về true, vì shape đang trỏ tới một object bị đóng băng.

Do đó, cộng với việc x không phải là object, ta sẽ không thể thay đổi giá trị của x. x sẽ vẫn là 10, và { x: 10, y: 20 } được ghi ra.

middle

C69:

Kết quả đoạn code sau là gì?

  function* generator(i) {
    yield i;
    yield i * 2;
  }
  const gen = generator(10);
  console.log(gen.next().value);
  console.log(gen.next().value);
  • A: [0, 10], [10, 20]
  • B: 20, 20
  • C: 10, 20
  • D: 0, 10 and 10, 20
Xem đáp án

Đáp án: C

Một hàm bình thường không thể bị dừng giữa chừng khi được gọi. Tuy nhiên một generator thì khác, nó có thể “dừng lại” được, và sau đó nó sẽ tiếp tục từ vị trí nó dừng lại. Mỗi khi một generator gặp một từ khóa yield, nó sẽ sinh ra giá trị ngay phía sau nó. Chú ý là generator không trả về giá trị, nó sinh ra giá trị.

Đầu tiên, chúng ta khởi tạo generator với giá trị i10. Generator được gọi bằng cách sử dụng phương thức next(). Khi lần đầu gọi thì i vẫn là 10. Khi nó bắt gặp từ khóa yield: nó sẽ sinh ra giá trị i. Generator sẽ được “tạm dừng” tại đây, và ghi ra giá trị 10.

Sau đó chung ta tiếp tục gọi generator bằng cách sử dụng tiếp phương thức next(). Nó sẽ bắt đầu từ vị trí nó tạm dừng lúc trước, khi i vẫn đang là 10. Và khi nó bắt gặp từ khóa yield, nó sẽ sinh ra giá trị i * 2. i10, nên nó sẽ sinh ra 10 * 2, tức 20. Vậy kết quả cuối cùng là 10, 20.

middle

C70:

Kết quả đoạn code sau là gì?

  const list = [1 + 2, 1 * 2, 1 / 2];
  console.log(list);
  • A: ["1 + 2", "1 * 2", "1 / 2"]
  • B: ["12", 2, 0.5]
  • C: [3, 2, 0.5]
  • D: [1, 1, 1]
Xem đáp án

Đáp án: C

Mảng có thể nhận bất cứ giá trị nào. Số, chuỗi, objects, mảng khác, null, boolean, undefined, và nhiều dạng biểu thức nữa như ngày tháng, hàm, và các tính toán.

Giá trị của phần tử chính là giá trị trả về. 1 + 2 trả về 3, 1 * 2 trả về 2, và 1 / 2 trả về 0.5.

middle

C71:

Giải thích về bubbling event và cách để ngăn chặn nó?

Xem đáp án

Bubbling event là khái niệm trong đó một sự kiện kích hoạt ở phần tử sâu nhất và kích hoạt trên các phần tử mẹ theo thứ tự lồng vào nhau. Do đó, khi click vào một phần tử con, sự kiện click của phần tử cha cũng được kích hoạt.

Một cách để ngăn sự kiện nổi bọt là sử dụng event.stopPropagation() hoặc event.cancelBubble trên IE < 9.

bubbling event trong javascript

middle

C72:

Kết quả đoạn code sau là gì?

  let randomValue = { name: "Lydia" };
  randomValue = 23;

  if (!typeof randomValue === "string") {
    console.log("It's not a string!");
  } else {
    console.log("Yay it's a string!");
  }
  • A: It's not a string!
  • B: Yay it's a string!
  • C: TypeError
  • D: undefined
Xem đáp án

Đáp án: B

Điều kiện trong mệnh đề if kiểm tra xem giá trị của !typeof randomValue bằng với "string" hay không. Phép toán ! chuyển giá trị đó thành giá trị boolean. Nếu giá trị là truthy, giá trị trả về sẽ là false, nếu giá trị là falsy, giá trị trả về sẽ là true. Trong trường hợp này, giá trị trả về của typeof randomValue là giá trị truthy "number", nghĩa là giá trị của !typeof randomValue là một giá trị boolean false.

!typeof randomValue === "string" luôn trả về false, vì ta thực sự đang kiểm tra false === "string". Vì điều kiện đã trả về false, code của mệnh đề else sẽ chạy và Yay it's a string! được in ra.

middle

C73:

Kết quả đoạn code sau là gì?

 class Dog {
   constructor(name) {
     this.name = name;
   }
 }

 Dog.prototype.bark = function () {
   console.log(`Woof I am ${this.name}`);
 };

 const pet = new Dog("Mara");

 pet.bark();

 delete Dog.prototype.bark;

 pet.bark();
  • A: "Woof I am Mara", TypeError
  • B: "Woof I am Mara","Woof I am Mara"
  • C: "Woof I am Mara", undefined
  • D: TypeError, TypeError
Xem đáp án

Đáp án: A

Chúng ta có thể xóa các thuộc tính khỏe object bằng từ khóa delete, kể cả với prototype. Khi chúng ta xóa một thuộc tính trên prototype, nó sẽ bị vô hiệu hóa hoàn toàn trong chuỗi prototype. Trong trường hợp này, hàm bark sẽ bị vô hiệu hóa ngay sau khi chúng ta thực hiện hàm xóa delete Dog.prototype.bark, tất nhiên ta vẫn có thể truy cập vào nó nhưng giá trị sẽ là undefined.

Khi chúng ta chạy một thứ không phải là hàm, nó sẽ bắn ra một TypeError. Trong trường hợp này là TypeError: pet.bark is not a function, vì bản thân thuộc tính pet.barkundefined.

middle

C74:

Làm sao để deep-freeze một đối tượng trong JavaScript?

Xem đáp án

Nếu bạn muốn đảm bảo đối tượng được deep-freeze (đóng băng sâu), bạn phải tạo một hàm đệ quy để freeze từng thuộc tính thuộc loại object:

without deep-freeze:

let person = {
  name: "Leonardo",
  profession: {
    name: "developer",
  },
};
Object.freeze(person); // make object immutable

person.profession.name = "doctor";
console.log(person); //output { name: 'Leonardo', profession: { name: 'doctor' } }

with deep-freeze:

function deepFreeze(object) {
  let propNames = Object.getOwnPropertyNames(object);
  for (let name of propNames) {
    let value = object[name];
    object[name] =
      value && typeof value === "object" ? deepFreeze(value) : value;
  }
  return Object.freeze(object);
}
let person = {
  name: "Leonardo",
  profession: {
    name: "developer",
  },
};
deepFreeze(person);
person.profession.name = "doctor"; // TypeError: Cannot assign to read only property 'name' of object

middle

C75:

Con trỏ This trong javascript được dùng để làm gì?

Xem đáp án

Trong javascript, chúng ta dùng từ khóa this để đại diện cho một đối tượng (Object). Đối tượng đó là chủ thế của ngữ cảnh, hoặc là chủ thế của code đang được chạy.

Ví dụ:

var person = {
  firstName: "kungfutech.edu.vn",
  lastName: "Vien Huynh",
  showName: function () {
    console.log(this.firstName + " " + this.lastName);
  },
};

//Ở đây this sẽ là object person
person.showName(); //kungfutech.edu.vn Vien Huynh

Một trường hợp khác, khi ta khai báo biến global và hàm global, toàn bộ các biến và hàm đó sẽ nằm trong một object có tên là window. Lúc này, khi ta gọi hàm showName, chính object window là object gọi hàm đó, this trỏ tới chính object window.

var firstName = "kungfutech.edu.vn",
  lastName = "Vien Huynh";
// 2 biến này nằm trong object window

function showName() {
  console.log(this.firstName + " " + this.lastName);
}

window.showName(); //kungfutech.edu.vn Vien Huynh - this trỏ tới object window
showName(); //kungfutech.edu.vn Vien Huynh - Object gọi hàm showName vẫn là object window

middle

C76:

Sự khác biệt giữa shimpolyfill trong Javascript là gì?

Xem đáp án

  • Một Shim là bất kỳ đoạn mã nào thực hiện việc chặn một lời gọi API và cung cấp một lớp trừu tượng. Nó không bị hạn chế đối với một ứng dụng web hay HTML5 / CSS3.

  • Một Polyfill là một loại Shim trang bị thêm cho các trình duyệt cũ với các tính năng HTML5 / CSS3 hiện đại, thường sử dụng Javascript hoặc Flash.

  • Một Shim là một thư viện mang một API mới đến một môi trường cũ, chỉ sử dụng các phương tiện của môi trường đó. Do đó, polyfill là một shim cho một API trình duyệt.

middle

C77:

Nêu một số trường hợp không nên sử dụng Arrow Functions trong ES6?

Xem đáp án

Các arrow functions KHÔNG nên được sử dụng:

  • Khi chúng ta muốn function hoisting - vì các Arrow Functions là ẩn danh.

  • Khi chúng ta muốn sử dụng this / arguments trong một hàm - vì các Arrow Functions không có this / arguments của riêng chúng, chúng phụ thuộc vào ngữ cảnh bên ngoài của chúng.

  • Khi chúng ta muốn sử dụng hàm được đặt tên (named function) - vì các Arrow Functions là ẩn danh.

  • Khi chúng ta muốn sử dụng hàm như một phương thức khởi tạo - vì các Arrow Functions không có chức năng này.

  • Khi chúng ta muốn thêm một thuộc tính là một hàm vào trong object literal và sử dụng đối tượng trong đó - vì chúng ta không thể truy cập vào điều này (mà phải là chính đối tượng).

middle

C78:

Kết quả đoạn code sau là gì?

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

const member = new Person("Lydia", "Hallie");
Person.getFullName = function () {
  return `${this.firstName} ${this.lastName}`;
};

console.log(member.getFullName());
  • A: TypeError
  • B: SyntaxError
  • C: Lydia Hallie
  • D: undefined undefined
Xem đáp án

Đáp án: A

Chúng ta không thể add thêm một thuộc tính cho một constructor giống như một object thông thường. Nếu bạn muốn add thêm thuộc tính nào đó cho tất cả các object một lần, bạn phải dùng prototype. Trong trường hợp này cũng vậy.

Person.prototype.getFullName = function () {
  return `${this.firstName} ${this.lastName}`;
};

khi này member.getFullName() sẽ hoạt động. Tại sao nên làm vậy? Hãy thử thêm chúng trực tiếp vào constructor xem sao. Không phải mọi instance Person đều cần phương thức này. Nó sẽ dẫn tới việc lãng phí rất nhiều bộ nhớ, khi chúng đều phải lưu trữ thuộc tính này cho mỗi instance. Thay vì thế, nếu ta chỉ thêm chúng vào prototype, ta sẽ chỉ tốn bộ nhớ một lần mà thôi, và mọi object khác đều có thể truy cập đến nó!

middle

C79:

Từ khóa new trong JavaScript là gì?

Xem đáp án

  • Nó tạo ra một đối tượng mới. Loại đối tượng này chỉ đơn giản là object.
  • Nó đặt thuộc tính internal, inaccessible, prototype (tức là proto) của đối tượng mới này trở thành đối tượng prototype, external, accessible của hàm khởi tạo (constructor function), mọi function object đều tự động có một thuộc tính prototype.
  • Nó làm cho biến this trỏ tới đối tượng mới được tạo.
  • Nó thực thi hàm khởi tạo, sử dụng đối tượng mới được tạo bất cứ khi nào điều này được đề cập.
  • Nó trả về đối tượng mới được tạo, trừ khi hàm khởi tạo trả về một tham chiếu đối tượng non-null. Trong trường hợp này, tham chiếu đối tượng đó được trả về thay thế.
function New(func) {
  var res = {};
  if (func.prototype !== null) {
    res.__proto__ = func.prototype;
  }
  var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
  if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
    return ret;
  }
  return res;
}

middle

C80:

Sự khác biệt giữa null, undefined hoặc undeclared là gì?

Xem đáp án

Các biến undeclared (chưa được khai báo) được tạo khi bạn gán một giá trị cho một identifier chưa được tạo trước đó bằng cách sử dụng var, let hoặc const. Các biến undeclared sẽ được xác định trên toàn bộ, bên ngoài của phạm vi hiện tại (current scope). Trong strict mode, một ReferenceError sẽ được ném ra khi bạn cố gắng gán tới một biến undeclared. Tránh chúng bằng mọi giá! Để kiểm tra chúng, hãy bọc chúng trong một khối try / catch.

function foo() {
  x = 1; // Throws a ReferenceError in strict mode
}

foo();
console.log(x); // 1

Một biến là undefined là một biến đã được khai báo, nhưng không được gán giá trị. Nếu một hàm không trả về bất kỳ giá trị nào được gán cho một biến, thì biến đó cũng có giá trị undefined. Để kiểm tra nó, hãy so sánh bằng cách sử dụng toán tử Strict Equality (===) hoặc typeof. Lưu ý rằng bạn không nên sử dụng toán tử Abstract Equality (==) để kiểm tra, vì nó cũng sẽ trả về true nếu giá trị là null.

var foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === "undefined"); // true

console.log(foo == null); // true. Wrong, don't use this to check!

function bar() {}
var baz = bar();
console.log(baz); // undefined

Một biến là null nếu được gán cho một giá trị null. Nó đại diện cho không giá trị (no-value) và khác với undefined là nó đã được gán một cách rõ ràng. Để kiểm tra null, chỉ cần so sánh bằng cách sử dụng toán tử Strict Equality(===). Lưu ý rằng giống như ở trên, bạn không nên sử dụng toán tử Abstract equality(==) để kiểm tra, vì nó cũng sẽ trả về true nếu giá trị là undefined.

var foo = null;
console.log(foo === null); // true
console.log(typeof foo === "object"); // true

console.log(foo == undefined); // true. Wrong, don't use this to check!

Theo thói quen cá nhân, tôi không bao giờ để các biến của mình là undeclared hoặc unassigned. Tôi sẽ gán null cho chúng một cách rõ ràng sau khi khai báo nếu tôi chưa có ý định sử dụng nó. Nếu bạn sử dụng linter (ví dụ ESLint) trong quy trình làm việc của mình, nó sẽ thường kiểm tra rằng bạn đang không tham chiếu tới các biến undeclared.

middle

C81:

Kết quả đoạn code sau là gì?

  const user = {
    email: "[email protected]",
    updateEmail: (email) => {
      this.email = email;
    },
  };

  user.updateEmail("[email protected]"); console.log(user.email);
Xem đáp án

Đáp án: A

Hàm updateEmail là một cú pháp arrow function và nó không gắn với user object. Điều này cho thấy từ khoá this không trỏ tới user object mà trỏ tới global scope. Giá trị của email trong user object không thay đổi. Khi ta in ra giá trị của user.email, nó trả về giá trị ban đầu của [email protected].

middle

C82:

IIFEs (Immediately Invoked Function Expressions) là gì?

Xem đáp án

Đó là một Immediately-Invoked Function Expression, gọi tắt là IIFE. Nó thực thi ngay sau khi được tạo:

(function IIFE() {
  console.log("Hello!");
})();
// "Hello!"

IIFEs thường được sử dụng khi cố gắng tránh làm rối global namespace, bởi vì tất cả các biến được sử dụng bên trong IIFE (giống như trong bất kỳ hàm thông thường nào khác) đều không thể sử dụng bên ngoài phạm vi của nó.

middle

C83:

Kết quả đoạn code sau là gì?

  const set = new Set();
  set.add(1);
  set.add("Lydia");
  set.add({ name: "Lydia" });
  for (let item of set) {
    console.log(item + 2);
  }
  • A: 3, NaN, NaN
  • B: 3, 7, NaN
  • C: 3, Lydia2, [object Object]2
  • D: "12", Lydia2, [object Object]2
Xem đáp án

Đáp án: C

Phép toán + không chỉ dùng để cộng các số, mà nó còn dùng để nối chuỗi nữa. Mỗi khi Javascript engine gặp một giá trị trong phép toán không phải dạng số, nó sẽ chuyển các số trong phép toán đó sang dạng chuỗi.

Phép toán đầu tiên item là một số 1, nên 1 + 2 trả về 3.

Ở phép toán thứ hai, item là một chuỗi "Lydia". trong khi đó 2 là một số, nên 2 sẽ bị chuyển sang dạng chuỗi, sau khi nối vào ta có chuỗi "Lydia2".

Ở phép toán thứ ba, { name: "Lydia" } là một object. Tuy nhiên dù có là object hay gì đi nữa thì nó cũng sẽ bị chuyển sang dạng chuỗi. Đối với object thì khi chuyển sang dạng chuỗi nó sẽ trở thành "[object Object]". "[object Object]" nối với "2" trở thành "[object Object]2".

middle

C84:

Kết quả đoạn code sau là gì?

let number = 0;
console.log(number++);
console.log(++number);
console.log(number);
  • A: 1 1 2
  • B: 1 2 2
  • C: 0 2 2
  • D: 0 1 2
Xem đáp án

Đáp án: C

Khi phép toán ++ nằm ở đằng sau (postfix):

  1. Trả về giá trị (trả về 0)
  2. Tăng giá trị lên (number giờ là 1)

Khi phép toán ++ nằm ở đằng trước (prefix):

  1. Tăng giá trị lên (number giờ là 2)
  2. Trả về giá trị (trả về 2)

Vậy kết quả là 0 2 2.

middle

C85:

Giải thích về ScopeScope Chain trong Javascript?

Xem đáp án

Scope trong JS, xác định khả năng truy cập của các biến, hàm ở các phần khác nhau trong một đoạn code.

Nói chung, Scope cho biết phạm vi mà biến và hàm của ta có thể hay không thể truy cập. Có 3 loại scope trong JS:

  • Global Scope
  • Local hay Function Scope
  • Block Scope

Global Scope

Các biến hoặc hàm được khai báo trong namespace global đều có global scope, có nghĩa là tất cả các biến và hàm có global scope có thể được truy cập từ bất kỳ đâu bên trong code.

var globalVariable = "Hello world";

function sendMessage() {
  return globalVariable; // can access globalVariable since it's written in global space
}

function sendMessage2() {
  return sendMessage(); // Can access sendMessage function since it's written in global space
}

sendMessage2(); // Returns “Hello world”

Function Scope

Bất kỳ biến hoặc hàm nào được khai báo bên trong một hàm đều có function scope, có nghĩa là tất cả các biến và hàm được khai báo bên trong một hàm, có thể được truy cập từ bất cứ đâu bên trong hàm chứ không phải bên ngoài nó.

function awesomeFunction() {
  var a = 2;

  var multiplyBy2 = function () {
    console.log(a * 2); // Can access variable "a" since a and multiplyBy2 both are written inside the same function
  };
}
console.log(a); // Throws reference error since a is written in local scope and cannot be accessed outside

multiplyBy2(); // Throws reference error since multiplyBy2 is written in local scope

Block Scope

Block Scope liên quan đến các biến được khai báo bằng letconst. Các biến được khai báo với var không có block scope.

Block scope cho chúng ta biết rằng bất kỳ biến nào được khai báo bên trong một khối {}, chỉ có thể được truy cập bên trong khối đó và không thể được truy cập bên ngoài khối đó.

{
  let x = 45;
}

console.log(x); // Gives reference error since x cannot be accessed outside of the block

for (let i = 0; i < 2; i++) {
  // do something
}

console.log(i); // Gives reference error since i cannot be accessed outside of the for loop block

Scope Chain

JavaScript Engine cũng sử dụng scope để tìm biến. Ví dụ:

var y = 24;

function favFunction() {
  var x = 667;
  var anotherFavFunction = function () {
    console.log(x); // Does not find x inside anotherFavFunction, so looks for variable inside favFunction, outputs 667
  };

  var yetAnotherFavFunction = function () {
    console.log(y); // Does not find y inside yetAnotherFavFunction, so looks for variable inside favFunction and does not find it, so looks for variable in global scope, finds it and outputs 24
  };

  anotherFavFunction();
  yetAnotherFavFunction();
}

favFunction();

Như bạn có thể thấy trong đoạn code trên, nếu javascript engine không tìm thấy biến trong function scope, nó sẽ cố gắng kiểm tra biến ở phạm vi bên ngoài. Nếu biến không tồn tại trong phạm vi bên ngoài, nó sẽ cố gắng tìm biến trong global scope.

Nếu biến cũng không được tìm thấy trong không gian chung, thì lỗi tham chiếu sẽ được đưa ra.

middle

C86:

Kết quả đoạn code sau là gì?

  const colorConfig = {
    red: true,
    blue: false,
    green: true,
    black: true,
    yellow: false,
  };
  const colors = ["pink", "red", "blue"];
  console.log(colorConfig.colors[1]);
  • A: true
  • B: false
  • C: undefined
  • D: TypeError
Xem đáp án

Đáp án: D

Trong Javascript ta có hai cách để truy cập thuộc tính của một object: sử dụng ngoặc vuông [], hoặc sử dụng chấm .. Trong trương hợp này chúng ta sử dụng chấm (colorConfig.colors) thay cho ngoặc vuông (colorConfig["colors"]).

Với cách sử dụng chấm, Javascript sẽ tìm kiếm một thuộc tính có tên chính xác như tên ta đưa vào. Trong trường hợp này nó là thuộc tính colors trong object colorConfig Tuy nhiên trong object này không có thuộc tính nào tên là colors, nên nó sẽ trả về undefined. Sau đó chúng ta cố truy cậ vào thuộc tính 1 của nó bằng cách gọi [1]. Chúng ta không thể làm như vậy trên giá trị undefined, nên nó sẽ trả về TypeError: Cannot read property '1' of undefined.

Javascript thông dịch theo câu lệnh. Khi ta sử dụng ngoặc vuông, Nnó sẽ tìm mở ngoặc đầu tiên [ và tiếp tục cho tới khi gặp đóng ngoặc tương ứng ]. Chỉ khi đó nó mới đánh giá câu lệnh. Nếu chúng ta sử dụng cú pháp colorConfig[colors[1]], nó sẽ trả về giá trị của thuộc tính red trong object colorConfig.

middle

C87:

Giải thích cách hoạt động của JSONP và tại sao nó không thực sự là Ajax?

Xem đáp án

Answer JSONP (JSON with Padding) là một phương pháp thường được sử dụng để vượt qua các chính sách cross-domain trong trình duyệt web bởi vì việc gọi Ajax từ trang hiện tại đến một cross-origin domain là không được cho phép.

JSONP hoạt động bằng cách đưa ra một request đến một cross-origin domain thông qua thẻ <script> và thường với một tham số là callback query, ví dụ: https://example.com?callback=printData. Sau đó, máy chủ sẽ bọc dữ liệu trong một hàm gọi là printData và trả lại cho máy khách.

<!-- https://mydomain.com -->
<script>
  function printData(data) {
    console.log(`My name is ${data.name}!`);
  }
</script>
<script src="https://example.com?callback=printData"></script>
// File loaded from https://example.com?callback=printData
printData({ name: "kungfutech.edu.vn" });

Máy khách phải có hàm printData trong phạm vi toàn cục của nó và hàm sẽ được thực thi bởi máy khách khi nhận được phản hồi từ cross-origin domain.

JSONP có thể không an toàn và có một số tác động bảo mật. Vì JSONP thực sự là JavaScript, nó có thể làm mọi thứ khác mà JavaScript có thể làm, vì vậy bạn cần tin tưởng nhà cung cấp dữ liệu JSONP.

Ngày nay, CORS là phương pháp được khuyến nghị và JSONP được coi là một cuộc tấn công (hack).

middle

C88:

Có thể reset một generator ES6 về state ban đầu của nó không?

Xem đáp án

Khi một generator bước sang trạng thái “completed”, nó sẽ không bao giờ rời khỏi nó và execution context liên quan của nó sẽ không bao giờ được tiếp tục.

Bất kỳ trạng thái thực thi nào được liên kết với generator cũng có thể bị loại bỏ tại thời điểm này.

middle

C89:

Hạn chế của phương thức private trong JavaScript là gì?

Xem đáp án

Một trong những hạn chế của phương thức private trong JavaScript là chúng rất tốn kém bộ nhớ vì một bản sao mới của phương thức sẽ được tạo cho mỗi trường hợp.

var Employee = function (name, company, salary) {
  this.name = name || ""; //Public attribute default value is null
  this.company = company || ""; //Public attribute default value is null
  this.salary = salary || 5000; //Public attribute default value is null

  // Private method
  var increaseSalary = function () {
    this.salary = this.salary + 1000;
  };

  // Public method
  this.dispalyIncreasedSalary = function () {
    increaseSalary();
    console.log(this.salary);
  };
};

// Create Employee class object
var emp1 = new Employee("John", "Pluto", 3000);
// Create Employee class object
var emp2 = new Employee("Merry", "Pluto", 2000);
// Create Employee class object
var emp3 = new Employee("Ren", "Pluto", 2500);

Ở đây mỗi biến emp1, emp2, emp3 đều có bản sao riêng của phương thức private increaseSalary. Vì vậy, không nên sử dụng phương thức private trừ khi nó cần thiết.

middle

C90:

Kết quả đoạn code sau là gì?

  let person = { name: "Lydia" };
  const members = [person];
  person = null;
  console.log(members);
  • A: null
  • B: [null]
  • C: [{}]
  • D: [{ name: "Lydia" }]
Xem đáp án

Đáp án: D

Đầu tiên, chúng ta khai báo một biến person là một object có thuộc tính name.

Sau đó chúng ta khai báo một biến members. Ta set giá trị đầu tiên của mảng là giá trị của biến person. Khi sử dụng gán bằng, object sẽ được tham chiếu tới object mà nó được gán. Khi ta gán tham chiếu từ một biến sang biến khác, ta tạo ra một bản sao của tham chiếu đó. (nên nhớ rằng đó vẫn là 2 tham chiếu hoàn toàn khác nhau!)

Sau đó ta set giá trị của person bằng null.

Chúng ta chỉ đơn thuần là thay đổi giá trị của biến person mà thôi, chứ không phải giá trị của phần tử đầu tiên ở trong mảng, vì chúng ta có một tham chiếu khác đến object đó. Phần tử đầu tiên của mảng members vẫn giữ tham chiêu đến object gốc. Do vậy, khi chúng ta in ra mảng members, phần tử đầu tiên sẽ vẫn in ra giá trị của objet gốc.

middle

C91:

Kết quả đoạn code sau là gì?

  const promise1 = Promise.resolve("First");
  const promise2 = Promise.resolve("Second");
  const promise3 = Promise.reject("Third");
  const promise4 = Promise.resolve("Fourth");

  const runPromises = async () => {
    const res1 = await Promise.all([promise1, promise2]);
    const res2 = await Promise.all([promise3, promise4]);
    return [res1, res2];
  };

  runPromises()
    .then((res) => console.log(res))
    .catch((err) => console.log(err));
  • A: [['First', 'Second'], ['Fourth']]
  • B: [['First', 'Second'], ['Third', 'Fourth']]
  • C: [['First', 'Second']]
  • D: 'Third'
Xem đáp án

Đáp án: D

Hàm Promise.all trả về những promise truyền vào song song nhau. Nếu một promise thất bại, hàm Promise.all rejects với giá trị của promise đó. Trong trường hợp này, promise3 bị reject với giá trị "Third". Ta đang kiểm tra giá trị bị reject trong chuỗi hàm catch khi goi hàm runPromises để tìm ra lỗi trong hàm runPromises. Chỉ có "Third" được trả về vì promise3 reject giá trị này.

middle

C92:

Output của đoạn code dưới đây là gì?

  var x = 1;
  var output = (function () {
    delete x;
    return x;
  })();
  console.log(output);
Xem đáp án

Đoạn mã trên có output là 1. Toán tử delete được sử dụng để xóa thuộc tính khỏi object. x ở đây không phải là một object mà là biến toàn cục (global variable) có kiểu là number.

middle

C93:

3 giai đoạn của event propagation là gì?

  • A: Target > Capturing > Bubbling
  • B: Bubbling > Target > Capturing
  • C: Target > Bubbling > Capturing
  • D: Capturing > Target > Bubbling
Xem đáp án

Đáp án: D

Trong capturing phase, event được truyền từ các phần tử cha cho tới phần tử target. Sau khi tới được phần tử target thì bubbling sẽ bắt đầu.

middle

C94:

Kết quả đoạn code sau là gì?

  const numbers = [1, 2, 3, 4, 5];
  const [y] = numbers;

  console.log(y);
  • A: [[1, 2, 3, 4, 5]]
  • B: [1, 2, 3, 4, 5]
  • C: 1
  • D: [1]
Xem đáp án

Đáp án: C

Chúng ta có thể unpack các giá trị từ mảng hoặc thuộc tính từ objects bằng phương pháp destructuring. Ví dụ:

[a, b] = [1, 2];

Giá trị của a sẽ là 1, b sẽ là 2. Thực tế, câu hỏi của chúng ta đơn giản là:

[y] = [1, 2, 3, 4, 5];

Có nghĩa là y chính là giá trị đầu tiên trong mảng, tức số 1. Do đó khi ta in ra y thì sẽ là1.

middle

C95:

Kết quả đoạn code sau là gì?

  const createMember = ({ email, address = {} }) => {
    const validEmail = /.+@.+..+/.test(email);
    if (!validEmail) throw new Error("Valid email pls");

    return {
      email,
      address: address ? address : null,
    };
  };

  const member = createMember({ email: "[email protected]" });
  console.log(member);
Xem đáp án

Đáp án: C

Giá trị mặc định của address là một object rỗng {}. Khi ta cho biến member bằng với object được trả về bởi hàm createMember, ta đã không truyền vào một giá trị của address, nghĩa là giá trị của address là object rỗng {} được mặc định. Object rỗng mang giá trị truthy, tức là điều kiện address ? address : null trả về true. Giá trị của address là một object rỗng {}.

middle

C96:

Khi nào cần sử dụng asyncdefer trong javascript?

Xem đáp án

Khi sử dụng Javascript trong HTML, chúng ta có thể sử dụng các thuộc tính asyncdefer để tối ưu hóa quá trình tải trang web và cải thiện trải nghiệm người dùng.

Thuộc tính async cho phép các script được tải và thực thi song song với quá trình phân tích HTML, điều này thích hợp với các script độc lập và không phụ thuộc vào nhau. Trong trường hợp này, việc sử dụng thuộc tính async sẽ giúp cải thiện tốc độ tải trang.

Thuộc tính defer cũng cho phép tải và thực thi script song song với quá trình phân tích HTML, nhưng việc thực thi sẽ diễn ra sau khi trình duyệt đã phân tích xong toàn bộ HTML. Điều này thích hợp cho các script phụ thuộc vào nhau hoặc được sử dụng lại. Trong trường hợp này, việc sử dụng thuộc tính defer sẽ giúp đảm bảo thứ tự thực thi của các script.

Tuy nhiên, nếu script của bạn rất nhỏ (chỉ vài dòng code), tốt nhất là sử dụng inline script, tức là đặt code script trực tiếp vào trong thẻ script, để tránh tải thêm một file JavaScript độc lập.

Tóm lại, việc hiểu rõ tính chất, nguyên lý của các thuộc tính asyncdefer sẽ giúp bạn sử dụng chúng đúng cách và tối ưu hóa tốc độ tải trang của website.

Ví dụ sử dụng async

<!DOCTYPE html>
<html>
  <head>
    <title>Async Example</title>
  </head>
  <body>
    <h1>Async Example</h1>
    <script async src="script.js"></script>
    <p>This paragraph may appear before the script is loaded and executed.</p>
  </body>
</html>

Trong ví dụ trên, script.js là một tập tin script external, và được đặt trong thẻ script với thuộc tính async. Điều này cho phép trang web tiếp tục render các phần khác của HTML trong khi trang web đang tải script.js. Khi script.js được tải và chạy xong, nó sẽ thực thi.

Ví dụ sử dụng defer

<!DOCTYPE html>
<html>
  <head>
    <title>Defer Example</title>
    <script defer src="script.js"></script>
  </head>
  <body>
    <h1>Defer Example</h1>
    <p>This paragraph may appear before the script is loaded, but it won't execute until the entire HTML document is parsed.</p>
  </body>
</html>

Trong ví dụ trên, script.js được đặt trong thẻ script với thuộc tính defer. Script này sẽ được tải trong quá trình phân tích HTML, nhưng nó sẽ không thực thi cho đến khi HTML được phân tích hoàn tất. Điều này giúp tăng tốc độ hiển thị trang web của bạn, vì nó cho phép trình duyệt render phần còn lại của HTML trước khi script.js được thực thi.

middle

C97:

Kết quả đoạn code sau là gì?

  let num = 10;

  const increaseNumber = () => num++;
  const increasePassedNumber = number => number++;

  const num1 = increaseNumber();
  const num2 = increasePassedNumber(num1);

  console.log(num1);
  console.log(num2);
  • A: 10, 10
  • B: 10, 11
  • C: 11, 11
  • D: 11, 12
Xem đáp án

Đáp án: A

Phép toán ++ sẽ trả về trước giá trị của toán hạng, sau đó tăng giá trị của toán hạng lên. Giá trị của num110, vì increaseNumber sẽ trả về giá trị của num, đang là 10, và sau đó mới tăng giá trị của num lên.

num2 cũng là 10, vì chúng ta đưa num1 vào increasePassedNumber. number bằng 10(tức giá trị của num1). Cũng giống như trên, phép toán ++ sẽ trả về trước giá trị của toán hạng, sau đó tăng giá trị của toán hạng lên. Giá trị của number10, do đó num2 cũng sẽ là 10.

middle

C98:

Giải thích sự khác biệt giữa undefinednot defined trong JavaScript?

Xem đáp án

Trong JavaScript nếu bạn sử dụng một biến không tồn tại và chưa được khai báo, thì JavaScript sẽ thảy ra một lỗi var name is not defined và sau đó script sẽ bị ngừng thực thi. Nhưng nếu bạn sử dụng typeof undeclared_variable thì nó sẽ trả về undefined.

Trước khi bắt đầu thảo luận thêm, hãy hiểu sự khác biệt giữa khai báo và định nghĩa.

var x là một khai báo vì bạn chưa xác định nó giữ giá trị nào, bạn chỉ đang khai báo sự tồn tại của nó và nhu cầu cấp phát bộ nhớ.

var x; // declaring x
console.log(x); //output: undefined

var x = 1 vừa là khai báo vừa là định nghĩa (cũng có thể nói là chúng ta đang khởi tạo), ở đây việc khai báo và gán giá trị xảy ra nội tuyến cho biến x, trong JavaScript mọi khai báo biến và khai báo hàm đều đưa lên đầu phạm vi (scope) hiện tại của nó, nó được khai báo sau đó việc gán diễn ra theo thứ tự, thuật ngữ này được gọi là hoisting.

Một biến được khai báo nhưng không được định nghĩa và khi chúng ta cố gắng truy cập vào nó, nó sẽ trả về kết quả là undefined.

var x; // Declaration
if(typeof x === 'undefined') // Will return true

Một biến không được khai báo và cũng không được định nghĩa thì khi chúng ta cố gắng tham chiếu đến biến đó, kết quả sẽ là not defined.

console.log(y); // Output: ReferenceError: y is not defined

middle

C99:

Kết quả đoạn code sau là gì?

  (() => {
    let x = (y = 10);
  })();

  console.log(typeof x);
  console.log(typeof y);
  • A: "undefined", "number"
  • B: "number", "number"
  • C: "object", "number"
  • D: "number", "undefined"
Xem đáp án

Đáp án: A

let x = y = 10; chính là cách viết ngắn gọn của:

y = 10;
let x = y;

Khi ta set y bằng 10, thực tế chúng ta đã sử dụng biến global y (window nếu là trên browser, global nếu là môi trường Node).Trên browser, window.y sẽ là 10.

Sau đó, chúng ta khai báo giá trị của x với giá trị của y, tức 10. Tuy nhiên khi ta khai báo với từ khóa let biến x sẽ chỉ tồn tại trong block scoped; hay trong trường hợp này là hàm thực hiện ngay lập tức (immediately-invoked function - IIFE). Khi ta sử dụng phép toán typeof, x hoàn toàn chưa được định nghĩa: vì x lúc này nằm bên ngoài block nó được định nghĩa lúc trước. Nghĩa là xundefined. Do đó console.log(typeof x) trả về "undefined".

Tuy nhiên với y thì khác, ta đã có giá trị của y khi set y bằng 10. Giá trị đó có thể truy cập được từ bất kì đâu bởi chúng là biến global. y được định nghĩa với kiểu là "number". Do đó console.log(typeof y) trả về "number".

middle

C100:

Yêu cầu bạn hãy xóa các phần tử duplicate trong mảng, bạn sẽ làm như thế nào?

Xem đáp án

Hãy sử dụng đối tượng Set(), Sets là một kiểu đối tượng mới trong ES6 (ES2015) cho phép tạo các collections có các giá trị không trùng nhau.

Các giá trị trong một set có thể là các giá trị nguyên thủy đơn giản như chuỗi hoặc số nguyên, hoặc các kiểu đối tượng phức tạp hơn như các đối tượng hoặc mảng. Ví dụ

const array = [1, 4, 99, 3, 1, 4, 15];
const noDups = Array.from(new Set(array));
console.log(noDups); //[1, 4, 99, 3, 15]

middle

C101:

Sự khác nhau giữa bind, callapply trong Javascript?

Xem đáp án

Cả ba phương thức bind, call, và apply trong JavaScript đều được sử dụng để thay đổi ngữ cảnh của hàm bằng cách thay đổi giá trị của từ khóa this và truyền đối số vào hàm. Tuy nhiên, có sự khác nhau giữa chúng:

callapply đều được sử dụng để gọi một hàm và thiết lập giá trị của từ khóa this bằng cách truyền nó vào như một đối số đầu tiên của hàm. Tuy nhiên, sự khác nhau giữa hai phương thức này là cách truyền đối số cho hàm:

  • call: truyền các đối số vào hàm dưới dạng danh sách các đối số, ví dụ: func.call(thisValue, arg1, arg2, ...).
  • apply: truyền các đối số vào hàm dưới dạng một mảng các đối số, ví dụ: func.apply(thisValue, [arg1, arg2, ...]).

bind cũng được sử dụng để thiết lập giá trị của từ khóa this, tuy nhiên nó không gọi hàm mà trả về một hàm mới với ngữ cảnh được thiết lập. Hàm mới này có thể được lưu trữ và sử dụng sau này. Phương thức bind có cú pháp như sau: var newFunc = func.bind(thisValue, arg1, arg2, ...)

Ví dụ:

const person = {
  firstName: "John",
  lastName: "Doe",
  fullName: function() {
    return `${this.firstName} ${this.lastName}`;
  }
}

const person2 = {
  firstName: "Jane",
  lastName: "Smith"
}

// sử dụng call
console.log(person.fullName.call(person2)); // Jane Smith

// sử dụng apply
console.log(person.fullName.apply(person2)); // Jane Smith

// sử dụng bind
const person3 = {
  firstName: "Mike",
  lastName: "Johnson"
}
const fullNameFunc = person.fullName.bind(person3);
console.log(fullNameFunc()); // Mike Johnson

Trong ví dụ trên, chúng ta có một đối tượng person với phương thức fullName để trả về tên đầy đủ của người đó. Sử dụng callapply, chúng ta có thể thay đổi ngữ cảnh của this để truy xuất đến tên của người khác. Sử dụng bind, chúng ta tạo ra một hàm mới và lưu trữ nó vào biến fullNameFunc để có thể sử dụng sau này.

middle

C102:

Sự khác biệt giữa từ khóa await và từ khóa yield là gì?

Xem đáp án

Answer yield có thể được coi là cơ sở xây dựng của await. yield nhận giá trị mà nó được cho và chuyển nó cho caller. Caller sau đó có thể làm bất cứ điều gì nó muốn với giá trị đó (1). Sau đó, caller có thể trả lại một giá trị cho generator (thông qua generate.next()), giá trị này sẽ trở thành kết quả của biểu thức yield (2) hoặc một lỗi sẽ được thảy ra bởi biểu thức yield (3).

async - await có thể được coi là sử dụng yield. Tại (1), caller (tức là trình điều khiển async - await - tương tự như chức năng bạn đã đăng) sẽ bọc giá trị trong một promise bằng cách sử dụng một thuật toán tương tự như new Promise(r => r(value)) (lưu ý, không phải là Promise.resolve, nhưng đó không phải là một vấn đề lớn). Sau đó nó sẽ đợi promise để resolve. Nếu nó hoàn thành, nó sẽ chuyển giá trị đã hoàn thành trở lại (2). Nếu nó reject, nó sẽ thảy ra lý do reject để làm lỗi ở (3).

Vì vậy, tiện ích của async - await là sử dụng yield để lấy giá trị thu được như một promise và truyền lại giá trị đã resolve của nó, lặp lại cho đến khi hàm trả về giá trị cuối cùng của nó.

middle

C103:

Kết quả đoạn code sau là gì?

function getPersonInfo(one, two, three) {
  console.log(one);
  console.log(two);
  console.log(three);
}

const person = "Lydia";
const age = 21;

getPersonInfo`${person} is ${age} years old`;
  • A: "Lydia" 21 ["", " is ", " years old"]
  • B: ["", " is ", " years old"] "Lydia" 21
  • C: "Lydia" ["", " is ", " years old"] 21
Xem đáp án

Đáp án: B

Nếu bạn dùng tagged template literals, giá trị của đối số đầu tiên luôn luôn là một mảng các string. Những đối số còn lại sẽ lấy giá trị từ biểu thức đưa vào!

middle

C104:

Phép toán này dùng để làm gì?

  JSON.parse();
  • A: Parse JSON thành một giá trị JavaScript
  • B: Parse một JavaScript object thành JSON
  • C: Parse giá trị JavaScript bất kì thành JSON
  • D: Parse JSON thành một JavaScript object
Xem đáp án

Đáp án: A

Với phương thức JSON.parse(), ta sẽ parse một chuỗi JSON thành một giá trị JavaScript.

Ví dụ:

// Chuyển một số thành một chuỗi JSON, sau đó parse chuỗi JSON đó để trả về một giá trị JavaScript:
const jsonNumber = JSON.stringify(4); // '4'
JSON.parse(jsonNumber); // 4

// Chuyển một mảng thành một chuỗi JSON, sau đó parse chuỗi JSON để trả về một giá trị JavaScript:
const jsonArray = JSON.stringify([1, 2, 3]); // '[1, 2, 3]'
JSON.parse(jsonArray); // [1, 2, 3]

// Chuyển một object thành một chuỗi JSON, sau đó parse chuỗi JSON để trả về một giá trị JavaScript:
const jsonArray = JSON.stringify({ name: "Lydia" }); // '{"name":"Lydia"}'
JSON.parse(jsonArray); // { name: 'Lydia' }

middle

C105:

Kết quả đoạn code sau là gì?

  console.log(String.raw`Hello
world`);
  • A: Hello world!
  • B: Hello
         world
  • C: Hello world
  • D: Hello
         world
Xem đáp án

Đáp án: C

String.raw trả về chuỗi nguyên bản, các ký tự ( , , etc.) sẽ vẫn là nguyên bản và không biến thành xuống dòng hay khoảng trắng! Nếu ta không để là chuỗi nguyên bản, sẽ có trường hợp xảy ra lỗi không mong muốn, ví dụ với đường dẫn:

const path = `C:DocumentsProjects able.html`

Sẽ cho ta chuỗi là:

"C:DocumentsProjects able.html"

Với String.raw, nó sẽ trả về là:

C:DocumentsProjects able.html

Do đó, trong trường hợp này Hello world sẽ được ghi ra.

middle

C106:

Sự khác biệt giữa MapWeakMap trong ES6 là gì?

Xem đáp án

Cả hai đều hoạt động khác nhau khi một đối tượng được tham chiếu bởi các keys/values của chúng bị xóa. Hãy lấy ví dụ dưới đây:

var map = new Map();
var weakmap = new WeakMap();

(function () {
  var a = {
    x: 12,
  };
  var b = {
    y: 12,
  };

  map.set(a, 1);
  weakmap.set(b, 2);
})();

Khi IIFE ở trên được thực thi, chúng ta không có cách nào có thể tham chiếu đến {x: 12}{y: 12} nữa. Trình thu gom rác sẽ hành động và xóa con trỏ khóa b khỏi “WeakMap” và cũng xóa {y: 12} khỏi bộ nhớ. Nhưng trong trường hợp Map, trình thu gom rác không xóa một con trỏ khỏi Map và cũng không xóa {x: 12} khỏi bộ nhớ.

WeakMap cho phép bộ thu gom rác thực hiện nhiệm vụ của nó nhưng Map thì không. Với các maps được viết tay, mảng các keys sẽ giữ các tham chiếu đến các đối tượng chính, ngăn chúng bị bộ thu gom rác thu thập. Trong WeakMaps, các tham chiếu đến các đối tượng chính được giữ “một cách yếu ớt”, có nghĩa là chúng không ngăn chặn việc bị bộ thu gom rác thu thập trong trường hợp không có tham chiếu nào khác đến đối tượng.

middle

C107:

Kết quả đoạn code sau là gì?

  function compareMembers(person1, person2 = person) {
    if (person1 !== person2) {
      console.log("Not the same!");
    } else {
      console.log("They are the same!");
    }
  }
  const person = { name: "Lydia" };
  compareMembers(person);
  • A: Not the same!
  • B: They are the same!
  • C: ReferenceError
  • D: SyntaxError
Xem đáp án

Đáp án: B

Object sẽ được truyền vào hàm theo reference. Khi chúng ta nói so sánh strict equal (===), nghĩa là ta đang so sánh các reference của chúng.

Ta set giá trị mặc định của person2 là object person, và đưa object person vào làm giá trị cho đối số person1.

Điều đó có nghĩa là chúng cùng trỏ đến một object trong bộ nhớ, do đó chúng bằng nhau, và They are the same! được in ra.

middle

C108:

Kết quả đoạn code sau là gì?

  const name = "Lydia";
  age = 21;

  console.log(delete name); console.log(delete age);
  • A: false, true
  • B: "Lydia", 21
  • C: true, true
  • D: undefined, undefined
Xem đáp án

Đáp án: A

Phép toán delete sẽ trả về một giá trị boolean: true nếu xóa thành công, false nếu thất bại. Tuy nhiên, nếu biến được khai báo với các từ khóa var, const hay let thì nó sẽ không thể bị xóa bởi phép toán delete.

Biến name được khai báo với từ khóa const, nên nó sẽ không thể bị xóa và trả về false. Khi ta set age bằng 21, thực tế là ta đang sử dụng biến global age. Ta có thể xóa sử dụng phép toán delete, khi này delete age trả về true.

middle

C109:

Generator trong Javascript là gì?

Xem đáp án

Generator là các chức năng có thể được exitre-entered sau đó. Context của chúng sẽ được lưu qua các lần truy cập lại. Các hàm Generator được viết bằng cú pháp

function* nameYourFuntion([param[, param[, ... param]]]) {
   statements
}

Trong đó thì:

  • nameYourFuntion: tên hàm
  • param: tham số đầu vào của hàm, tối đa 255 tham số
  • statements: phần thân chứa nội dung của hàm.

Khi chúng ta gọi Generator function nameYourFuntion() , nó không trả về các kiểu dữ liệu cơ bản mà đẩy về một iterator object. Hàm next() của iterator object được sử dụng để truy xuất các node dữ liệu sau mỗi bước resume lại generator function. Khi đó generator function sẽ thực thi hàm cho đến khi gặp từ khóa yield , hoặc return kế tiếp chưa được duyệt ở bước trước. (iterator định nghĩa một chuẩn để tạo ra list các giá trị hữu hạn hoặc thậm chí vô hạn. Nó giúp bạn lấy ra giá trị mong muốn khi list kết quả đã được khởi tạo xong toàn bộ.)

Yield được sử dụng ở một vài nơi và có vẻ khái niệm cũng hơi khác nhau. Về cơ bản, yeild là từ khóa dùng để tạm dừng và cũng để tiếp tục việc thực thi bên trong generator function. Ví dụ

function* generatorFunc(index) {
  while (index < 2) {
    yield index++;
  }
}

const iterator = generatorFunc(0);

console.log(iterator.next());
// log output: {value : 0, done : false}

console.log(iterator.next());
// log output: {value : 1, done : false}

console.log(iterator.next());
// log output: {value : underfined, done : true}

Như đã đề cập ở trên thì iterator được khởi tạo bằng generatorFunc với index bắt đầu bằng 0. Bạn có thể thấy yeild ở đây, trong ví dụ này chính là một phiên bản khác giống như return vậy. Nó trả về một đối tượng IteratorResult với hai thuộc tính là “value” và “done”.

value : kết quả của biểu thức trả về.
done : nhận giá trị false nếu quá trình generator chưa hoàn thành, true nếu ngược lại.

Giá trị của index được giữ lại sau mỗi lần chúng ta gọi next() và tất nhiên là cả ngữ cảnh của hàm generator cũng thế cho đến khi toàn bộ yield, return đã được duyệt qua..

middle

C110:

Làm thế nào có thể ghi ra giá trị giống như trong comment khi console.log?

  function* startGame() {
    const answer = yield "Do you love JavaScript?";
    if (answer !== "Yes") {
      return "Oh wow... Guess we're gone here";
    }
    return "JavaScript loves you back ❤️";
  }

  const game = startGame();
  console.log(/* 1 */); // Do you love JavaScript?
  console.log(/* 2 */); // JavaScript loves you back ❤️
  • A: game.next("Yes").value and game.next().value
  • B: game.next.value("Yes") and game.next.value()
  • C: game.next().value and game.next("Yes").value
  • D: game.next.value() and game.next.value("Yes")
Xem đáp án

Đáp án: C

Một generator sẽ “tạm dừng” khi nhìn thấy từ khóa yield. Đầu tiên ra sẽ đưa ra chuỗi “Do you love JavaScript?”, bằng cách gọi game.next().value.

Chương trình sẽ chạy từng dòng, cho tới khi nó tìm thấy từ khóa yield. Có một từ khóa yield tại dòng đầu tiên của hàm: chương trình sẽ dừng tại đâ! Điều đó có nghĩa là biến answer chưa hề được định nghĩa!

Khi ta gọi game.next("Yes").value, yield trước đó sẽ được thay thế bởi giá trị được truyền vào hàm next(), trong trường hợp này là"Yes". Theo đó giá trị của biến answer giờ sẽ là "Yes". Điều kiện if sẽ trả về false, và JavaScript loves you back ❤️ sẽ được ghi ra.

middle

C111:

Kết quả đoạn code sau là gì?

function sum(a, b) {
  return a + b;
}

sum(1, "2");
  • A: NaN
  • B: TypeError
  • C: "12"
  • D: 3
Xem đáp án

Đáp án: C

JavaScript là một ngôn ngữ dynamically typed: chúng ta không khai báo kiểu dữ liệu khi khai báo biến. Giá trị có thể bị tự động convert sang một kiểu dữ liệu khác mà ta không hề hay biết, điều này được gọi là implicit type coercion. Coercion có nghĩa là convert từ kiểu này sang kiểu khác.

Trong ví dụ này, JavaScript sẽ convert số 1 sang dạng string. Mỗi khi ta cộng một số (1) với một string ('2'), số sẽ luôn được xem như là một string. Kết quả sẽ là một phép nối chuỗi giống như "Hello" + "World", vậy nên "1" + "2" sẽ trả về là "12".

middle

C112:

Symbol trong ES6 là gì?

Xem đáp án

Symbol là một loại đối tượng mới, đặc biệt, có thể được sử dụng như một tên thuộc tính duy nhất trong các đối tượng.

Sử dụng Symbol thay vì string cho phép các modules khác nhau tạo ra các thuộc tính không xung đột với nhau. Các symbols cũng có thể được đặt ở chế độ riêng tư, để các thuộc tính của chúng không thể bị truy cập bởi những ai chưa có quyền truy cập trực tiếp vào Symbol.

Symbol là một loại primitive mới. Cũng giống như các primitive khác như number, string và boolean, Symbol có một hàm được sử dụng để tạo chúng. Không giống như các primitive khác, Symbols không có literal syntax (Literal là một giá trị mà nó thể hiện chính nó, ví dụ: số 12 — thể hiện là một giá trị kiểu number, hay “Javascript” — đại diện cho một giá trị kiểu string) - cách duy nhất để tạo chúng là với hàm tạo Symbol theo cách sau:

let symbol = Symbol();

Trên thực tế, các Symbol chỉ là một cách hơi khác để gắn các thuộc tính vào một đối tượng - bạn có thể dễ dàng cung cấp các Symbol thường dùng dưới dạng các phương thức chuẩn, giống như Object.prototype.hasOwnProperty xuất hiện trong mọi thứ kế thừa từ Object.

middle

C113:

So sánh sự khác nhau giữa Object.freeze()const?

Xem đáp án

Answer constObject.freeze là hai thứ hoàn toàn khác nhau:

So sánh sự khác nhau giữa Object.freeze() và const

  • const áp dụng cho các ràng buộc biến. Nó tạo ra một ràng buộc bất biến, tức là bạn không thể gán một giá trị mới cho ràng buộc đó.
const person = {
  name: "Leonardo",
};
let animal = {
  species: "snake",
};
person = animal; // ERROR "person" is read-only
  • Object.freeze hoạt động trên các giá trị và cụ thể hơn là các giá trị đối tượng. Nó làm cho một đối tượng trở nên bất biến, tức là bạn không thể thay đổi các thuộc tính của nó.
let person = {
  name: "Leonardo",
};
let animal = {
  species: "snake",
};
Object.freeze(person);
person.name = "Lima"; // TypeError: Cannot assign to read only property 'name' of object
console.log(person);

middle

C114:

Kết quả đoạn code sau là gì?

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

const lydia = new Person("Lydia", "Hallie");
const sarah = Person("Sarah", "Smith");

console.log(lydia);
console.log(sarah);
  • A: Person {firstName: "Lydia", lastName: "Hallie"}undefined
  • B: Person {firstName: "Lydia", lastName: "Hallie"}Person {firstName: "Sarah", lastName: "Smith"}
  • C: Person {firstName: "Lydia", lastName: "Hallie"}{}
  • D:Person {firstName: "Lydia", lastName: "Hallie"}ReferenceError
Xem đáp án

Đáp án: A

Với sarah, chúng ta khai báo mà không có từ khóa new. Khi sử dụng new, nó sẽ trỏ đến một object mới mà ta vừa tạo ra. Tuy nhiên nếu ta không dùng new thì nó sẽ trỏ tới global object!

Chúng ta cho rằng this.firstName"Sarah"this.lastName"Smith". Tuy nhiên sự thực là chúng ta đã định nghĩa global.firstName = 'Sarah'global.lastName = 'Smith'. Bản thân biến sarah vẫn là undefined.

middle

C115:

Sự khác nhau giữa Anonymous functionNamed function là gì?

Xem đáp án

Anonymous function là một hàm không có tên, thường được sử dụng để truyền vào các hàm khác như là một tham số hoặc định nghĩa trực tiếp trong một biểu thức.

Ví dụ:

var multiply = function(x, y) {
  return x * y;
};

Named function là một hàm được đặt tên và được định nghĩa riêng biệt, có thể được gọi nhiều lần trong chương trình.

function multiply(x, y) {
  return x * y;
}

Sự khác biệt chính giữa Anonymous function và Named function là Named function có tên và được sử dụng để tái sử dụng trong chương trình, trong khi Anonymous function được sử dụng để truyền vào các hàm khác như là một tham số hoặc định nghĩa trực tiếp trong một biểu thức.

middle

C116:

Kết quả đoạn code sau là gì?

function getAge() {
  "use strict";
  age = 21;
  console.log(age);
}

getAge();
  • A: 21
  • B: undefined
  • C: ReferenceError
  • D: TypeError
Xem đáp án

Đáp án: C

Với "use strict", chúng ta sẽ đảm bảo được rằng ta sẽ không bao giờ khai báo biến global một cách vô ý. Tại đây chúng ta chưa khai báo biến age, và khi dùng "use strict", nó sẽ throw ra một reference error. Nếu như không dùng "use strict", nó sẽ vẫn hoạt động, vì thuộc tính age sẽ được thêm vào global object.

middle

C117:

Đệ quy là gì?

Xem đáp án

Đệ quy là một kỹ thuật lặp đi lặp lại một hoạt động bằng cách tự gọi hàm lặp đi lặp lại cho đến khi nó đi đến kết quả.

function add(number) {
  if (number <= 0) {
    return 0;
  } else {
    return number + add(number - 1);
  }
}

add(3) => 3 + add(2)
          3 + 2 + add(1)
          3 + 2 + 1 + add(0)
          3 + 2 + 1 + 0 = 6  

Ví dụ về một hàm đệ quy:

Hàm sau đây tính tổng của tất cả các phần tử trong một mảng bằng cách sử dụng đệ quy:

function computeSum(arr){
  if(arr.length === 1){
    return arr[0];
  }
  else{
    return arr.pop() + computeSum(arr);
  }
}

computeSum([7, 8, 9, 99]); // Returns 123

middle

C118:

Giải thích chính sách same-origin trong JavaScript?

Xem đáp án

Same-origin policy (SOP) là một trong những chính sách bảo mật quan trọng nhất trên trình duyệt hiện đại, nhằm ngăn chặn JavaScript code có thể tạo ra những request đến những nguồn khác với nguồn mà nó được trả về. Ba tiêu chí chính để so sánh request bao gồm:

  • Domain (tên miền)
  • Protocol (giao thức)
  • Port (cổng kết nối)

Nói đơn giản thì request sẽ được coi là hợp lệ chỉ khi nó thỏa mãn 3 tiêu chí ở trên (cùng domain,cùng protocol và cùng port)

Giải thích chính sách same-origin trong JavaScript

Ví dụ: khi chúng ta đang mở 2 tab, 1 tab là facebook, tab kia là 1 trang web nào đó có chứa mã độc. Sẽ rất nguy hiểm nếu như các đoạn script ở bên tab chứa mã độc có thể tự do thao tác lên tab facebook phía bên kia, và SOP sinh ra với nhiệm vụ ngăn chặn các hành động này.

Dưới đây là vd về list các pages vi phạm SOP của site origin( http://www.example.com) :

middle

C119:

Cho một ví dụ về curry và tại sao cú pháp này mang lại lợi ích?

Xem đáp án

Currying là một pattern trong đó một hàm có nhiều hơn một tham số được chia thành nhiều hàm, khi được gọi nối tiếp, sẽ tích lũy tất cả các tham số được yêu cầu cùng một lúc.

Kỹ thuật này có thể hữu ích để làm cho mã được viết theo kiểu functional dễ đọc và soạn thảo hơn. Điều quan trọng cần lưu ý là để một hàm được xử lý, nó cần bắt đầu như một hàm, sau đó được chia nhỏ thành một chuỗi các hàm mà mỗi hàm chấp nhận một tham số.

function curry(fn) {
  if (fn.length === 0) {
    return fn;
  }
  function _curried(depth, args) {
    return function (newArgument) {
      if (depth - 1 === 0) {
        return fn(...args, newArgument);
      }
      return _curried(depth - 1, [...args, newArgument]);
    };
  }
  return _curried(fn.length, []);
}

function add(a, b) {
  return a + b;
}

var curriedAdd = curry(add);
var addFive = curriedAdd(5);

var result = [0, 1, 2, 3, 4, 5].map(addFive); // [5, 6, 7, 8, 9, 10]

middle

C120:

Kết quả đoạn code sau là gì?

  function greeting() {
    throw "Hello world!";
  }
  function sayHi() {
  try {
  const data = greeting();
  console.log("It worked!", data);
  } catch (e) {
  console.log("Oh no an error!", e);
  }
  }
  sayHi();
  • A: "It worked! Hello world!"
  • B: "Oh no an error: undefined
  • C: SyntaxError: can only throw Error objects
  • D: "Oh no an error: Hello world!
Xem đáp án

Đáp án: D

Với lệnh throw, chúng ta có thể tạo ra các errors tùy ý. Với câu lệnh đó, chúng ta có thể throw các exception. Một exeption có thể là một chuỗi, một số, một boolean hoặc một object. Trong trường hợp này thì nó là chuỗi 'Hello world'.

Với lệnh catch chúng ta có thể xử lý những exeption được throw ra khi thực hiện try. Một exeption đã được throw ra: chuỗi 'Hello world'. e chính là chuỗi đó và chúng ta sẽ in ra. Kết quả là 'Oh an error: Hello world'.

middle

C121:

Kết quả đoạn code sau là gì?

function checkAge(data) {
  if (data === { age: 18 }) {
    console.log("You are an adult!");
  } else if (data == { age: 18 }) {
    console.log("You are still an adult.");
  } else {
    console.log(`Hmm.. You don't have an age I guess`);
  }
}

checkAge({ age: 18 });
  • A: You are an adult!
  • B: You are still an adult.
  • C: Hmm.. You don't have an age I guess
Xem đáp án

Đáp án: C

Khi test sự bằng nhau, các kiểu dữ liệu cơ bản sẽ so sánh giá trị của chúng, còn object thì so sánh tham chiếu. JavaScript sẽ kiểm tra xem các object đó có trỏ đến những vùng nhớ giống nhau hay không.

Hai object chúng ta đang so sánh không có được điều đó: object đối số tham chiếu đến một vùng nhớ khác với object chúng ta dùng để kiểm tra sự bằng nhau.

Đó là lý do tại sao cả { age: 18 } === { age: 18 }{ age: 18 } == { age: 18 } đều trả về false.

middle

C122:

Kết quả đoạn code sau là gì?

  const user = { name: "Lydia", age: 21 };
  const admin = { admin: true, ...user };

  console.log(admin);
  • A: { admin: true, user: { name: "Lydia", age: 21 } }
  • B: { admin: true, name: "Lydia", age: 21 }
  • C: { admin: true, user: ["Lydia", 21] }
  • D: { admin: true }
Xem đáp án

Đáp án: B

Ta có thể kết hợp 2 object sử dụng phép toán spread operator .... Nó cho phép ta tạo ra bản sao của từng cặp key/values trong từng object và nối chúng lại với nhau thành một object mới. Trong trường hợp này chúng ta tạo ra các bản sao của các cặp key/value của object user object, và nối chúng vào object admin. admin object khi này sẽ trở thành { admin: true, name: "Lydia", age: 21 }.

middle

C123:

Giải thích sự khác nhau giữa hai cách sử dụng dưới đây

  function Person() {}
  var person = Person();
  var person = new Person();
Xem đáp án

Câu hỏi này khá mơ hồ. Dự đoán tốt nhất của tôi về ý định của nó là nó đang hỏi về các hàm tạo trong JavaScript. Về mặt kỹ thuật, function Person() {} chỉ là một khai báo hàm bình thường. Quy ước là sử dụng PascalCase cho các hàm được sử dụng làm hàm tạo.

var person = Person() gọi Person dưới dạng một hàm chứ không phải là một hàm tạo. Gọi như vậy là một sai lầm phổ biến nếu hàm được dự định sử dụng như một phương thức khởi tạo. Thông thường, hàm tạo không trả về bất kỳ thứ gì, do đó việc gọi hàm tạo giống như một hàm bình thường sẽ trả về undefined và được gán cho biến dùng để làm instance.

var person = new Person() tạo một instance của đối tượng Person bằng toán tử new, toán tử này kế thừa từ Person.prototype. Một giải pháp thay thế là sử dụng Object.create, ví dụ: Object.create(Person.prototype).

function Person(name) {
  this.name = name;
}
var person = Person("John");
console.log(person); // undefined
console.log(person.name); // Uncaught TypeError: Cannot read property 'name' of undefined

var person = new Person("John");
console.log(person); // Person { name: "John" }
console.log(person.name); // "john"

middle

C124:

Prototype trong Javascript là gì?

Xem đáp án

Tất cả các đối tượng javascript đều kế thừa các thuộc tính từ một prototype.

Ví dụ:

Đối tượng Date kế thừa các thuộc tính từ prototype Date.

Đối tượng Math kế thừa các thuộc tính từ prototype Math.

Đối tượng Array kế thừa các thuộc tính từ prototype Array.

Trên đầu chuỗi là Object.prototype. Mọi prototype đều kế thừa các thuộc tính và phương thức từ Object.prototype.

Prototype là một bản thiết kế của một đối tượng. Prototype cho phép chúng ta sử dụng các thuộc tính và phương thức trên một đối tượng ngay cả khi các thuộc tính và phương thức không tồn tại trên đối tượng hiện tại.

prototype trong javascript

Ví dụ:

var arr = [];
arr.push(2);

console.log(arr); // Outputs [2]

Trong đoạn code trên, có thể thấy ta chưa xác định bất kỳ thuộc tính hoặc phương thức nào được gọi là push trên mảng arr nhưng javascript engine không đưa ra lỗi.

Lý do là việc sử dụng các prototype. Như đã thảo luận trước đây, các đối tượng Array kế thừa các thuộc tính từ prototype Array.

Javascript engine thấy rằng phương thức push không tồn tại trên đối tượng mảng hiện tại, do đó nó tìm kiếm phương thức push bên trong prototype Array và nó tìm thấy phương thức.

Bất cứ khi nào thuộc tính hoặc phương thức không được tìm thấy trên đối tượng hiện tại, javascript engine sẽ luôn tìm kiếm trong prototype của nó và nếu nó vẫn không tồn tại, nó sẽ tìm bên trong prototype của prototype, v.v.

senior

C125:

Prototype Design Pattern trong Javascript như thế nào?

Xem đáp án

Prototype Pattern là một object-based. Nó tạo ra một phiên bản mới của đối tượng dựa trên đối tượng nguyên mẫu. Mục tiêu chính là tạo ra một đối tượng sử dụng làm làm bản thiết kế cho mỗi đối tượng được tạo sau đó.

Nếu bạn cảm thấy khởi tạo một đối tượng mới trực tiếp quá phức tạp và không hiệu quả, trong trường hợp này, sử dụng Prototype Pattern là một cách ý tưởng không tồi.

Bạn có thể triển khai Prototype Pattern theo các như sau:

// Khởi tạo đối tượng
function Person(name, age) {
  this.name = name;
  this.age = age;

  this.showName = () => console.log(this.name);
}

// Clone prototype của đối tượng
function PersonPrototype(prototype) {
  const _prototype = prototype;

  this.clone = () => {
    let person = new Person();
    person.name = _prototype.name;
    person.age = _prototype.age;

    return person;
  };
}

const person = new Person("Codestus.com", 20);
const prototypeOfPerson = new PersonPrototype(person);
const tester = prototypeOfPerson.clone();

tester.showName(); // "Codestus.com"

Ngoài ra, bạn cũng có thể sử dụng khái niệm kế thừa prototype đã tích hợp sẵn trong đối tượng Object.

const person = {
  name: "Codestus.com",
  age: 20,
};

const personA = Object.assign({}, person);

senior

C126:

Temporal Dead Zone trong ES6 là gì?

Xem đáp án

Trong ES6, letconst được hoisting (giống như var, classfunction), nhưng có một khoảng thời gian giữa phạm vi truy cập và được khai báo là nơi chúng không thể được truy cập. Khoảng thời gian này là Temporal Dead Zone (TDZ).

Chúng ta cùng xem ví dụ dưới đây:

// console.log(aLet) // would throw ReferenceError

let aLet;
console.log(aLet); // undefined
aLet = 10;
console.log(aLet); // 10

Trong ví dụ này, Temporal Dead Zone kết thúc khi aLet được khai báo, thay vì được gán.

senior

C127:

Thuật ngữ transpiling là gì?

Xem đáp án

Không có cách nào để polyfill các cú pháp mới đã được thêm vào ngôn ngữ. Vì vậy, lựa chọn tốt hơn là sử dụng một công cụ chuyển đổi mã mới hơn của bạn thành các mã tương đương cũ hơn. Quá trình này thường được gọi là transpiling, một thuật ngữ để chuyển đổi + biên dịch (transforming + compiling).

Thông thường, bạn chèn transpiler (trình chuyển đổi) vào quy trình phát triển của mình, tương tự như trình ghép mã (linter) hoặc trình thu nhỏ (minifier) của bạn. Có khá nhiều bộ chuyển đổi tuyệt vời cho bạn lựa chọn:

  • Babel: Chuyển ES6+ thành ES5

  • Traceur: Chuyển ES6, ES7 và hơn thế nữa sang ES5

senior

C128:

Command Pattern trong Javascript như thế nào?

Xem đáp án

Mục đích của Command Pattern là đóng gói các hành động dưới dạng đối tượng. Cho phép phân tách hệ thống các đối tượng nhận yêu cầu với các đối tượng thực thi yêu cầu, có khả năng ghép nối. Trong đó, các yêu cầu được gọi là sự kiện và các mã để thực thi yêu cầu được gọi là trình xử lý sự kiện.

Nào bây giờ hãy tưởng tượng bạn nhận được yêu cầu xây dựng hệ thống thanh toán cho một cửa hàng thương mại điện tử. Tuỳ thuộc vào loại phương thức thanh toán, bạn sẽ chọn cho mình một quy trình cụ thể.

Ví dụ

if (paymentMethod === "creditcard") {
  // Xử lý thanh toán
}

Một số phương thức thanh toán chỉ cần một bước duy nhất, một số khác thì không. Dựa vào ví dụ trên, chúng ta sẽ thử xây dựng quy trình thanh toán.

Command Pattern là một giải pháp tốt để áp dụng trong ví dụ này. Cụ thể về ví dụ, hệ thống xử lý của bạn sẽ không biết nhiều thông tin về việc xử lý từng phương thức thanh toán như thế nào. Các yêu cầu xử lý thanh toán và nơi xử lý thanh toán sẽ tách biệt và được ghép nối lại từ hệ thống xử lý.

// Phần lõi điều hướng xử lý
function Command(operation) {
  this.operation = operation;
}

Command.prototype.execute = function () {
  this.operation.execute();
};

// Hàm phương thức thanh toán
function ProccessPaypalPayment() {
  return {
    execute: function () {
      console.log("Payment with Paypal");
    },
  };
}

// Hàm phương thức thanh toán
function ProccessCreditCardPayment() {
  return {
    execute: function () {
      console.log("Payment with Credit Card");
    },
  };
}

// Hàm xử lý thanh toán nhận yêu cầu và điều hướng xử lý thực thi
function PaymentHandler() {
  let paymentCommand;
  return {
    setPaymentCommand(command) {
      this.paymentCommand = command;
    },
    executeCommand() {
      this.paymentCommand.execute();
    },
  };
}

function main() {
  let systemPayment = new PaymentHandler();
  systemPayment.setPaymentCommand(new Command(new ProccessPaypalPayment()));
  systemPayment.executeCommand();
}

main();

senior

C129:

Closure trong javascript là gì, cho ví dụ?

Xem đáp án

Trong javascript, closure là một chức năng có quyền truy cập vào phạm vi cha, ngay cả sau khi scope đã đóng.

Đầu tiên bạn phải hiểu về scope như đã nói ở trên? Scope chính là tuổi thọ của một biến trong javascript. Bạn có thể thấy trong đó một biến được định nghĩa đóng một vai trò lớn trong khoảng thời gian của biến đó và các hàm trong chương trình của bạn có quyền truy cập vào đó. Ví dụ:

Khi sử dụng closure

function createCounter() {
let count = 0;
return function() {
  count++;
  console.log(count);
}
}

const counter = createCounter();

counter(); // in ra 1
counter(); // in ra 2
counter(); // in ra 3

Trong đoạn code trên, hàm createCounter() trả về một hàm lồng nhau, và biến count là một biến cục bộ (local variable) của hàm createCounter(). Tuy nhiên, khi chúng ta gọi hàm createCounter() và lưu kết quả vào biến counter, các lần gọi sau của hàm counter đều có thể truy cập và thay đổi giá trị của biến count, ngay cả khi hàm createCounter() đã kết thúc thực thi. Điều này là do hàm counter tạo ra một closure, cho phép nó truy cập vào biến count của hàm bên ngoài.

Closure còn được sử dụng rất nhiều trong các pattern thiết kế và thư viện JavaScript, ví dụ như IIFE (Immediately Invoked Function Expression), module pattern, debounce/throttle function, và nhiều hơn nữa.

senior

C130:

Constructor Design Pattern trong Javascript như thế nào?

Xem đáp án

Trong các ngôn ngữ lập trình hướng đối tượng, constructor là một phương thức đặt biệt được sử dụng để khởi tạo đối tượng mới cùng với các thuộc tính khởi tạo ban đầu.

Trong JavaScript, hầu như mọi thứ đều là một object và chúng ta thường quan tâm đến việc khởi tạo đối tượng với Object Constructor.

// Sử dụng {}
const person  = {};

// Sử dụng Object()
const person = new Object();
Ngoài ra, bạn cũng có thể khởi tạo 1 constructor bằng function

function Person() {}

const personA = new Person();

personA.name = "Codestus.com";

senior

C131:

Singleton Design Pattern trong Javascript như thế nào?

Xem đáp án

Đây là một design pattern vô cùng nổi tiếng, chúng ta sử dụng singleton pattern để hạn chế khởi tạo đối tượng, giảm bớt được khai báo đối tượng dư thừa, chỉ khởi tạo một lần duy nhất và có thể truy cập toàn cục. Đây sẽ là một pattern vô cùng hữu ích cho các trường hợp bạn phải xử lý 1 tác vụ ở nhiều nơi, có thể hạn chế được số lần khai báo đối tượng không cần thiết. Cùng thử xem ví dụ:

const utils = (() => {
  let instance;
  function initialize() {
    return {
      sum: function () {
        let nums = Array.prototype.slice.call(arguments);
        return nums.reduce((numb, total) => numb + total, 0);
      },
    };
  }
  return {
    getInstance: function () {
      // Nếu đối tượng này chưa được khởi tạo
      if (!instance) {
        // Khởi tạo lần đầu tiên
        instance = new initialize();
      }
      // Không khởi tạo nữa, chỉ trả về đối tượng đã khởi tạo
      return instance;
    },
  };
})();

const firstU = utils.getInstance(); // Cùng lấy 1 instance
const secondU = utils.getInstance(); // Cùng lấy 1 instance

console.log(firstU === secondU); // Trả về true là đúng vì cùng thuộc 1 instance duy nhất

console.log(firstU.sum(1, 2, 3, 4, 5)); // 15 // working

senior

C132:

Currying function trong Javascript là gì?

Xem đáp án

Currying là khi bạn chia nhỏ một hàm có nhiều đối số thành một chuỗi các hàm có một phần đối số. Đây là một ví dụ trong JavaScript:

function add(a, b) {
  return a + b;
}
add(3, 4); // returns 7

Đây là một hàm nhận hai đối số, ab và trả về tổng của chúng. Bây giờ chúng ta sẽ dùng curry cho hàm này:

function add(a) {
  return function (b) {
    return a + b;
  };
}

Trong đại số học, việc xử lý các hàm có nhiều đối số (hoặc tương đương với một đối số là N-tuple) hơi không phù hợp. Vì vậy, làm thế nào để bạn đối phó với một cái gì đó bạn muốn diễn đạt một cách tự nhiên, chẳng hạn như, f(x, y)?. Vâng, bạn coi điều đó tương đương với f(x)(y) - f(x), gọi nó là g, là một hàm, và bạn áp dụng hàm đó cho y.

Nói cách khác, bạn chỉ có các hàm nhận một đối số - nhưng một số hàm trong số đó trả về các hàm khác (Cũng nhận một đối số).

Ví dụ bạn có một hàm để tính giá trị discount, giảm ngay 10% cho khách hàng thân thiết.

function discount(price, discount) {
  return price * discount;
}
// Giảm ngay 50 đồng khi khách hàng đã tiêu 500 đồng.
const price = discount(500, 0.1); // $50
// $500  - $50 = $450

Khách hàng tiêu tiền điên cuồng, chúng ta liên tục gọi hàm

const price = discount(1500, 0.1); // $150
// $1,500 - $150 = $1,350
const price = discount(2000, 0.1); // $200
// $2,000 - $200 = $1,800
const price = discount(50, 0.1); // $5
// $50 - $5 = $45
const price = discount(5000, 0.1); // $500
// $5,000 - $500 = $4,500
const price = discount(300, 0.1); // $30
// $300 - $30 = $270

Chúng ta có thể đưa vào giá trị discount ở lần đầu tiên, đến các lần gọi tiếp theo, chúng ta ko cần truyền giá trị 10% này nữa

function discount(discount) {
  return price => {
    return price * discount;
  };
}
const tenPercentDiscount = discount(0.1);
tenPercentDiscount(500); // $50
const twentyPercentDiscount = discount(0.2);
twentyPercentDiscount(500); // 100
// $500 - $100 = $400
twentyPercentDiscount(5000); // 1000
// $5,000 - $1,000 = $4,000
twentyPercentDiscount(1000000); // 200000
// $1,000,000 - $200,000 = $600,000

Nói một cách ngắn gọn, khi cần truyền vào 1 argument ít thay đổi, cố định trong đa số các trường hợp, nghĩ đến currying.

senior

C133:

Giải thích Prototype Inheritance trong JavaScript là gì?

Xem đáp án

Trong một ngôn ngữ thực hiện kế thừa cổ điển như Java, C # hoặc C ++, bạn bắt đầu bằng cách tạo một class - một bản thiết kế cho các đối tượng của bạn - và sau đó bạn có thể tạo các đối tượng mới từ class đó hoặc bạn có thể mở rộng class, xác định một class mới để tăng cường class ban đầu.

Trong JavaScript, trước tiên bạn tạo một đối tượng (không có khái niệm về class trong Javascript), sau đó bạn có thể tăng cường đối tượng của riêng mình hoặc tạo các đối tượng mới từ nó. Mọi đối tượng trong Javascript đều có một prototype. Hệ thống kế thừa của JavaScript là nguyên mẫu và không dựa trên class. Khi một thông báo đến một đối tượng, JavaScript sẽ cố gắng tìm một thuộc tính trong đối tượng đó trước, nếu không thể tìm thấy thì thông báo sẽ được gửi đến prototype của đối tượng, v.v. Hành vi đó được gọi là prototype chain (chuỗi nguyên mẫu) hoặc prototype inheritance.

Hàm tạo (constructor) là cách được sử dụng nhiều nhất trong JavaScript để tạo prototype chain. Khi chúng ta sử dụng new, JavaScript đưa một tham chiếu ngầm đến đối tượng mới đang được tạo dưới dạng từ khóa this. Nó cũng trả về tham chiếu này một cách ngầm định ở cuối hàm.

function Foo() {
  this.kind = "foo";
}
var foo = new Foo();
foo.kind; //=> foo

senior

C134:

Sử dụng Promise trong JavaScript như thế nào?

Xem đáp án

Promise dùng cho xử lý bất đồng bộ trong JavaScript

Trước promise, callback được dùng cho các thao tác bất đồng bộ. Nhưng callback có giới hạn của nó, nếu sử dụng quá nhiều callback code sẽ trở nên khó quản lý.

Đối tượng promise có 4 trạng thái:

  • Pending: trạng thái bắt đầu, biểu diễn promise không phải là fulfilled, cũng không phải là rejected mà đang ở trạng thái pending.
  • Fulfilled: trạng thái này có nghĩa là thao tác bất đồng bộ đã hoàn tất.
  • Rejected: trạng này này có nghĩa là thao tác đã thất bại vì một vài lý do nào đó.
  • Settked: trạng thái này thể hiện promise đã rejected hoặc fulfilled.

Một promise được tạo bằng cách sử dụng phương thức khởi tạo Promise, hàm này nhận một hàm callback với hai tham số, resolvereject tương ứng.

  • resolve hàm được gọi, khi thao tác bất đồng bộ thực hiện thành công.
  • reject hàm được gọi, khi thao tác thất bại bởi một vài lỗi nào đó.

Ví dụ:

Promise được dùng cho các thao tác bất đồng bộ như yêu cầu của server, để dễ hiểu ta lấy ví dụ với một phép toán để tính tổng của ba phần tử.

function sumOfThreeElements(...elements){
  return new Promise((resolve,reject)=>{
    if(elements.length > 3 ){
      reject("Only three elements or less are allowed");
    }
    else{
      let sum = 0;
      let i = 0;
      while(i < elements.length){
        sum += elements[i];
        i++;
      }
      resolve("Sum has been calculated: "+sum);
    }
  })
}

Trong đoạn code trên, ta đang tính tổng của ba phần tử, nếu độ dài của mảng phần tử lớn hơn 3, thì promise sẽ bị rejected, ngược lại thì promise sẽ được resolved và tổng được trả về.

Chúng ta có thể sử dụng bất kỳ promise nào bằng cách gắn các phương thức then()catch() vào đối tượng sử dụng.

  • then() phương này được truy cập khi kết quả của promise là fulfilled.
  • catch() phương này được truy cập khi kết quả của promise là rejected.

Ví dụ:

sumOfThreeElements(4, 5, 6)
.then(result=> console.log(result))
.catch(error=> console.log(error));
// In the code above, the promise is fulfilled so the then() method gets executed

sumOfThreeElements(7, 0, 33, 41)
.then(result => console.log(result))
.catch(error=> console.log(error));
// In the code above, the promise is rejected hence the catch() method gets executed

senior

C135:

Higher-Order Function trong Javascript là gì?

Xem đáp án

Một higher-order function là một hàm nhận một hoặc nhiều hàm làm đối số, hàm này sử dụng để hoạt động trên một số dữ liệu hoặc trả về một hàm khác. Các higher-order functions có nghĩa là trừu tượng hóa một số hoạt động được thực hiện lặp đi lặp lại.

Ví dụ cổ điển về điều này là map, nhận một mảng và một hàm làm đối số, sau đó map sử dụng hàm này để biến đổi từng item trong mảng, trả về một mảng mới với dữ liệu đã biến đổi. Các ví dụ phổ biến khác trong JavaScript là forEach, filterreduce

Một higher-order function không nhất thiết chỉ được thao tác với các mảng mà còn có nhiều trường hợp sử dụng để trả về một hàm từ một hàm khác, ví dụ như Function.prototype.bind trong JavaScript.

** Ví dụ 1:** chúng ta có một mảng các số và chúng ta muốn tạo một mảng mới gấp đôi mỗi giá trị của mảng đầu tiên. Hãy để xem làm thế nào chúng ta có thể giải quyết vấn đề có và không có Higher-Order Functions.

Không Higher-order function

const arr1 = [1, 2, 3];
const arr2 = [];
for (let i = 0; i < arr1.length; i++) {
  arr2.push(arr1[i] * 2);
}
// prints [ 2, 4, 6 ]
console.log(arr2);

Có Higher-order function

const arr1 = [1, 2, 3];
const arr2 = arr1.map(function (item) {
  return item * 2;
});
console.log(arr2);

Chúng ta cũng có thể làm code ngắn hắn bằng cách sử dụng cú pháp arrow function:

const arr1 = [1, 2, 3];
const arr2 = arr1.map(item => item * 2);
console.log(arr2);

senior

C136:

Những công dụng của WeakMap trong ES6 là gì?

Xem đáp án

WeakMap chỉ có trong ES6 trở lên. WeakMap cung cấp một cách thức để mở rộng các đối tượng từ bên ngoài mà không can thiệp vào việc thu gom rác (garbage collection). Bất cứ khi nào bạn muốn mở rộng một đối tượng nhưng không thể vì nó bị bịt kín - hoặc từ một nguồn bên ngoài - thì một WeakMap có thể được áp dụng.

WeakMap là một tập hợp các cặp khóa và giá trị, trong đó khóa phải là một đối tượng.

var map = new WeakMap();
var pavloHero = {
  first: "Pavlo",
  last: "Hero",
};
var gabrielFranco = {
  first: "Gabriel",
  last: "Franco",
};
map.set(pavloHero, "This is Hero");
map.set(gabrielFranco, "This is Franco");
console.log(map.get(pavloHero)); //This is Hero

Khía cạnh thú vị của WeakMap là nó giữ một tham chiếu yếu (weak reference) đến khóa bên trong map. Tham chiếu yếu có nghĩa là nếu đối tượng bị phá hủy, bộ thu gom rác (garbage collector) sẽ xóa toàn bộ entry khỏi WeakMap, do đó sẽ giải phóng bộ nhớ.

senior

C137:

Giải thích về Hoisting trong Javascript?

Xem đáp án

Hoisting là một hành vi mặc định trong Javascript, nó sẽ chuyển tất cả khai báo biến và hàm lên trên cùng.

Hoisting trong Javascript

Điều này có nghĩa là bất kể hàm và biến được khai báo ở đâu, chúng cũng sẽ đuọc chuyển lên đầu scope. Scope có thể là toàn cục hoặc cục bộ.

Ví dụ 1:

hoistedVariable = 3;
console.log(hoistedVariable);
// output là 3 vì biến được khởi tạo trước khi khai báo.
var hoistedVariable;

Ví dụ 2:

hoistedFunction();
// Outputs " Hello world! " kể cả khi hàm được khai báo sau khi gọi.

function hoistedFunction() {
  console.log(" Hello world! ");
}

Ví dụ 3:

// Hoisting takes place in the local scope as well
function doSomething() {
  x = 33;
  console.log(x);
  var x;
}

Lưu ý: Khai báo biến được hoisting chứ phép gán biến thì không.

var x;
console.log(x); // Output sẽ trả về "undefined" vì phép gán không được hoisting
x = 23;

Lưu ý: Để tránh hoisting bạn có thể dùng “use strict”

"use strict";
x = 23; // Báo lỗi x  chưa được khai báo
var x;

senior