Khi bạn có thể sử dụng biến JavaScript trước khi khai báo, đó là do cơ chế hoisting (nâng biến). Parser đọc qua toàn bộ function trước khi thực thi.
Hành vi biến dường như được dùng trước khi khai báo gọi là hoisting:
Ví dụ
points = 200; var points;
Code trên hoạt động giống hệt:
var points; points = 200;
Giải thích kỹ thuật
JavaScript engine (V8, SpiderMonkey...) trong Creation Phase của Execution Context sẽ:
- Quét toàn bộ scope (global/function) tìm các khai báo
var,function. - Tạo binding trong Variable Environment:
var→undefined,function→ reference đến function object. - Sau đó mới chạy code line-by-line (Execution Phase).
Hoisting với var vs let/const (ES6)
| Keyword | Hoisted? | Giá trị khởi tạo | Temporal Dead Zone (TDZ) |
|---|---|---|---|
var | Có | undefined | Không |
let | Có | Uninitialized | Có (ReferenceError nếu truy cập trước khai báo) |
const | Có | Uninitialized | Có |
function | Có (toàn bộ function) | Function object | Không |
class | Có | Uninitialized | Có |
Ví dụ TDZ (Temporal Dead Zone)
console.log(a); // undefined (var hoisted)
console.log(b); // ReferenceError: Cannot access 'b' before initialization (let TDZ)
console.log(c); // ReferenceError (const TDZ)
var a = 1;
let b = 2;
const c = 3;
Function Hoisting
Function Declaration được hoisting toàn bộ (name + body):
sayHello(); // ✅ Hoạt động
function sayHello() {
console.log("Hello!");
}
Function Expression (gán function cho biến) chỉ hoisting biến (var/let/const), không hoisting function body:
sayHi(); // ❌ TypeError: sayHi is not a function (var) hoặc ReferenceError (let/const)
var sayHi = function() {
console.log("Hi!");
};
Best Practice
- Luôn khai báo biến (
constưu tiên,letnếu reassign) trước khi dùng. - Dùng
const/letthayvar(block-scoped, TDZ giúp bắt bug sớm). - Function declaration an toàn để gọi trước khi define, nhưng code style hiện đại khuyến khích định nghĩa trước khi dùng.
- Eslint rule
no-use-before-definegiúp catch lỗi hoisting.