Javascript | "Hoisting" trong javascript


Hoisting là gì ?

Về cơ bản, khi Javascript compiles tất cả đoạn code mà bạn viết ra, tất cả các biến mà bạn khai báo bằng cách sử dụng var sẽ tự động được đẩy lên đầu của function (nếu được khai báo bên trong một function) hoặc lên đầu của file biên dịch (nếu được khai báo bên ngoài của một function) - đó gọi là Hoisting.
Hãy hiểu rằng, việc hoisting (dịch ra thì đại loại là đẩy dòng code lên) không thực sự xảy ra với đoạn code mà bạn viết ra, mà việc khi trình biên dịch Javascript đọc qua đoạn mã của bạn thôi. Nên đừng bạn tâm nhiều về vấn đề đó.
Hãy hình dung là hoisting sẽ đữa tất cả những thứ mình khai báo bằng var lên đầu, nhưng trên lý thuyết thì không có bất cứ thứ gì được thay đổi ở đây @@.
Để có thể hiểu rõ ràng hơn về vấn đề này, mình sẽ đưa ra một vài ví dụ cơ bản để chứng minh tác động của việc hoisting.
Hãy thử với đoạn mã dưới đây trong phạm vi global scope:

console.log(myName);
var myName = ‘Ta Bich Phuong’;

Thứ gì sẽ hiện ra với console.log() kia?

  1. Uncaught ReferenceError: myName is not defined
  2. Ta Phuong Thao
  3. undefined

Khi chạy file js có chứa đoạn mã kia bạn sẽ nhận ra rằng lựa chọn thứ 3 mới là câu trả lời chính xác.
Như đã nêu ở trên, các biến sẽ được di chuyển lên đầu của một scope khi trình biên dịch Javascript chạy đoạn code của bạn(ngoại trừ việc sử dụng NodeJS - ở mức rất cơ bản chỉ đơn giản là trang web của bạn đang tải). Tuy nhiên, một lưu ý quan trọng cần phải nhớ ở đây là chỉ có duy nhất các biến mới được di chuyển lên đâu thôi, chứ không phải giá trị của biến đó.
Chỉ cần nắm rõ câu note trên là được. Có một câu hỏi khá là hay ở đây là: cho 1 file .js, tại dòng thứ 10 chúng ta khai báo: var myName = Ta Phuong Thao thì khi file js đó được compiled đoạn khai báo biến myName sẽ ở đâu?, đoạn gán giá trị biến myName sẽ ở đâu?

Hãy đọc lại đoạn code javascript ở trên, và sau đó bạn hãy xem lại cách mà trình Javascript biên dịch ở dưới đây
var myName;
console.log(myName);
myName = ‘Ta Phuong Thao’;

Đây là lý do tại sao console.log ở trên trả về giá trị undefined, đơn giản là biến myName đã tồn tại nhưng đơn giản là giá trị lại được khái bảo ở dòng thứ 3.

Vậy hãy thử xem ví dụ sau

function hey() {
console.log('hey ' + myName);
};
hey();
var myName = 'Ta Phuong Thao';

Function hey() sẽ vẫn trả về giá trị undefined, thực sự thì trình thông dịch Javascript sẽ biên dịch theo thời gian chạy:

function hey() {
console.log('hey ' + myName);
};
var myName;
hey();
myName = 'Ta Phuong Thao';

Vậy nên tại thời điểm function hey() được gọi, nó sẽ biết rằng có một biến được gọi đến là myName, nhưng biến này lại chưa được gán trị vào.

Vậy thì let và const thì sao?

Thực tế thì chúng cũng được hoisted, var, let, const, functionclass đều được hoisted. Điều quan trọng chúng ta cần biết là chúng không thật sự được đẩy lên đâu, chỉ có mỗi khai báo tên biến thôi, còn việc gán giá trị thì vẫn giữ nguyên vị trí.
Điểm khác biệt giữa var, letconst chính là việc khai báo chúng - hiểu đơn gian là cách truyền vào giá trị cho các biến khi khai báo.
Đối với varlet thì có thể khai báo biến mà không cần truyền vào giá trị gì, trong khi đó const sẽ quẳng về cho ta cái lỗi Reference error nếu bạn có tình kháo báo một biến mà không truyền vào giá trị gì. Vậy nên đó là lý do mà const myName = 'Ta Phuong Thao' sẽ hoạt động, còn const myName; myName='Ta Phuong Thao'; thì không.
Với varlet, bạn có thể sử dụng var để khai báo và sau đó sẽ nhận được giá trị undefined, tuy nhiên, nếu làm tương tự với let thì lại nhận được lỗi Reference Error

Vậy thì điểm khác nhau var, let và const khi hoisting là gì?

Nếu bạn tạo một biến bằng var ở phạm vị global, thì lúc đó nó sẽ tạo ra một thuộc tình trong scope đó - ví dụ với việc bạn inspect brower, rồi khai báo var myName = 'Ta Phuong Thao'; thì có thể gọi lại biến đó thông quá window.myName (scope ở đây là window).
Tuy nhiên, nếu bạn thay thế bằng let myName = 'Ta Phuong Thao'; thì lúc này sẽ không tạo ra thuộc tính trong scope đo, dẫn đến không thế gọi thông qua window.myName.

Hoisting có ảnh hưởng như thế nào với đoạn code của bạn?

Các biến được khai báo bằng var có thể truy cập bên ngoài scope(thông qua cách gọi property của scope đó) trong khi khai báo bằng letconst thì không thể.
Như chúng ta có thể thấy trong ví dụ dưới đây, khai báo bằng var sẽ trả về giá trị undefined trong khi đó sẽ nhận được lỗi nếu sử dụng letconst

console.log(1a’, myName1); // undefined
if (1) {
 console.log(1b’, myName1); // undefined
 var myName1 = ‘Ta Phuong Thao’;
}

console.log('2a', myName2); // error: myName2 is not defined
if (1) {
    console.log('2b', myName2); // undefined
    let myName2 = 'Ta Phuong Thao';
}

console.log('3a', myName3); // error: myName3 is not defined
if (1) {
    console.log('3b', myName3); // undefined
    const myName3 = 'Ta Phuong Thao';
}

All Rights Reserved