Hoisting trong JavaScript

Hoisting trong JavaScript là một cơ chế mặc định mà JavaScript “di chuyển” (hoist) các khai báo biến và hàm lên đầu phạm vi của chúng trong quá trình biên dịch, trước khi code thực thi. Điều này có nghĩa là các biến và hàm có thể được sử dụng trước khi chúng được khai báo trong code.

Cách hoạt động của hoisting:

  1. Hoisting biến:
    • Trong trường hợp của biến, chỉ có khai báo được hoisted, không phải giá trị khởi tạo.
    • Ví dụ:
      console.log(x); // undefined
      var x = 5;
      

      Trong đoạn code trên, biến x được hoisted lên đầu phạm vi và khởi tạo với giá trị undefined. Khi console.log(x) được gọi, giá trị của xundefined chứ không phải 5, vì giá trị 5 chỉ được gán sau đó.

  2. Hoisting với letconst:
    • Với letconst, biến cũng được hoisted nhưng không thể truy cập trước khi chúng được khai báo rõ ràng trong “temporal dead zone” (vùng chết tạm thời). Việc sử dụng biến trước khi khai báo sẽ gây ra lỗi.
    • Ví dụ:
      console.log(y); // ReferenceError: Cannot access 'y' before initialization
      let y = 10;
      
  3. Hoisting hàm:
    • Với các hàm khai báo (function declaration), cả phần khai báo và định nghĩa của hàm đều được hoisted. Do đó, bạn có thể gọi hàm trước khi nó được định nghĩa trong code.
    • Ví dụ:
      greet(); // Hello!
      function greet() {
          console.log("Hello!");
      }
      

      Trong đoạn code trên, hàm greet() có thể được gọi trước khi nó được định nghĩa.

  4. Hoisting hàm ẩn danh (function expression):
    • Đối với hàm ẩn danh hoặc arrow function gán cho biến, chỉ có phần khai báo biến được hoisted chứ không phải phần định nghĩa hàm. Điều này sẽ gây lỗi nếu bạn gọi hàm trước khi định nghĩa.
    • Ví dụ:
      sayHello(); // TypeError: sayHello is not a function
      var sayHello = function() {
          console.log("Hello!");
      }
      

Chi tiết về phạm vi hoisting:

  1. Phạm vi toàn cục:
    • Khi bạn khai báo biến hoặc hàm trong phạm vi toàn cục (bên ngoài bất kỳ hàm nào), các khai báo này sẽ được hoisted lên đầu của phạm vi toàn cục.
    • Ví dụ:
      console.log(globalVar); // undefined
      var globalVar = 10;
      
  2. Phạm vi hàm:
    • Khi bạn khai báo biến hoặc hàm bên trong một hàm, các khai báo này sẽ được hoisted lên đầu phạm vi của hàm đó.
    • Ví dụ:
      function test() {
          console.log(localVar); // undefined
          var localVar = 20;
      }
      test();
      
      
      
  3. Phạm vi khối (Block Scope):
    • Đối với các khai báo sử dụng letconst, hoisting vẫn xảy ra, nhưng các biến này không thể được truy cập trước khi chúng được khai báo do nằm trong “Temporal Dead Zone” (TDZ). Điều này có nghĩa là mặc dù chúng được hoisted lên, nhưng chúng không có giá trị cho đến khi dòng khai báo được thực thi.

Sự khác biệt giữa let, var, và const trong JavaScript liên quan đến hoistingtemporal dead zone (TDZ)

1. Hoisting

  • Tất cả các biến được khai báo bằng var, let, và const đều được hoisted lên đầu phạm vi của chúng.
  • Điều này có nghĩa là khi mã được biên dịch, JavaScript sẽ biết về sự tồn tại của các biến này, nhưng cách mà chúng được khởi tạo và truy cập khác nhau.

2. Khởi tạo

  • var:
    • Biến được khai báo bằng var sẽ được hoisted và khởi tạo với giá trị undefined ngay khi hoisting diễn ra.
    • Bạn có thể truy cập biến này trước khi nó được khai báo trong mã mà không gặp lỗi, nhưng giá trị của nó sẽ là undefined.Ví dụ:
      console.log(x); // Output: undefined
      var x = 10;
      console.log(x); // Output: 10
      
      

letconst:

  • Biến được khai báo bằng letconst cũng được hoisted, nhưng chúng không được khởi tạo cho đến khi dòng mã mà chúng được khai báo được thực thi.
  • Nếu bạn cố gắng truy cập biến let hoặc const trước khi nó được khai báo, bạn sẽ gặp lỗi ReferenceError do nó nằm trong temporal dead zone.Ví dụ:
    console.log(y); // ReferenceError: Cannot access 'y' before initialization
    let y = 20;
    console.log(y); // Output: 20
    
    console.log(z); // ReferenceError: Cannot access 'z' before initialization
    const z = 30;
    console.log(z); // Output: 30
    
    

Kết luận:

  • var: Hoisted và khởi tạo với giá trị undefined. Có thể truy cập trước khi khai báo mà không gặp lỗi.
  • letconst: Hoisted nhưng không khởi tạo ngay lập tức. Không thể truy cập trước khi khai báo, và sẽ gặp lỗi ReferenceError nếu bạn cố gắng làm như vậy.
  • Temporal Dead Zone (TDZ): Vùng mà biến letconst chưa được khởi tạo và không thể truy cập cho đến khi đến dòng khai báo.