Có, bạn có thể. Hiện tượng này gọi là Hoisting (nâng lên) – trình phân tích JavaScript di chuyển việc khai báo biến (không phải gán giá trị) lên đầu phạm vi (scope) trước khi thực thi mã.
Cơ chế Hoisting
Khi engine JavaScript biên dịch code, nó quét toàn bộ hàm/script để tìm các khai báo var, function, let, const, class và "nâng" phần khai báo lên đầu scope. Phần gán giá trị (= value) giữ nguyên vị trí.
Ví dụ với var
console.log(rank); // undefined (không báo lỗi ReferenceError)
var rank = 5;
console.log(rank); // 5
// Engine hiểu như sau:
var rank; // Khai báo được nâng lên
console.log(rank); // undefined
rank = 5; // Gán giá trị giữ nguyên chỗ
console.log(rank); // 5
Lưu ý quan trọng: let và const cũng bị hoisting nhưng khác
- Chúng bị nâng lên nhưng ở Temporal Dead Zone (TDZ) từ đầu scope đến dòng khai báo.
- Truy cập trước khai báo gây
ReferenceError.
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 10;
Function Declaration vs Function Expression
// Function Declaration: cả tên và body đều được hoisting
foo(); // "Hello"
function foo() { console.log("Hello"); }
// Function Expression (var): chỉ tên được hoisting
bar(); // TypeError: bar is not a function
var bar = function() { console.log("World"); };
// Function Expression (let/const): TDZ
baz(); // ReferenceError
let baz = function() { console.log("!"); };
Tại sao hiểu Hoisting quan trọng?
- Tránh bug lạ khi dùng biến trước khi gán giá trị (giá trị sẽ là
undefined). - Hiểu tại sao
function declarationcó thể gọi trước khi định nghĩa. - Viết code an toàn: luôn khai báo biến ở đầu scope (hoặc dùng
let/const).
Khuyến nghị
- Dùng
letvàconst(ES6+) thay vìvar– TDZ giúp phát hiện lỗi sớm. - Khai báo biến ở đầu hàm/block.
- Sử dụng linter (ESLint) với rule
no-use-before-define.