Hoisting là cách JavaScript engine tạo binding cho các khai báo trước khi thực thi code từng dòng trong scope.
Bạn có thể hình dung như ban tổ chức lập danh sách chỗ ngồi trước khi khách đến. Tên đã có trong danh sách, nhưng ghế có thể chưa sẵn sàng để ngồi.
Về cơ chế, mỗi scope có giai đoạn tạo trước giai đoạn thực thi. Engine quét scope và đăng ký binding trong lexical environment. var được tạo và gán undefined ngay, nên truy cập sớm trả về undefined. let, const và class cũng được đăng ký nhưng ở trạng thái chưa khởi tạo, tạo ra Temporal Dead Zone (TDZ) từ đầu scope đến dòng khai báo; truy cập trong TDZ ném ReferenceError. function declaration được tạo và khởi tạo cùng lúc với toàn bộ thân hàm, nên bạn có thể gọi trước khi dòng khai báo xuất hiện. Ngược lại, function expression, arrow function hay var fn = function(){} chỉ hoist phần biến var, không hoist thân hàm.
Ví dụ phân biệt ba trường hợp:
console.log(a); // undefined - var đã hoist và khởi tạo
var a = 1;
fn(); // gọi được vì function declaration hoist cả thân hàm
function fn() { return "hoisted"; }
// let/const đang trong TDZ cho tới dòng khai báo
try {
console.log(b); // ReferenceError
} catch (e) {
console.log(e instanceof ReferenceError); // true
}
let b = 2;typeofvới biếnlet/consttrong TDZ vẫn némReferenceError, không trả về"undefined".varcó scope là function, không phải block, nên hoisting có thể làm biến rò rỉ ra ngoàiif/for.- Khai báo
functiontrong block (if { function foo(){} }) từng có hành vi khác nhau giữa các engine; ở strict mode hiện tại nó có scope block, đừng dựa vào việc gọi trước block.
Lưu ý: Dùng let/const và khai báo hàm/biến trước khi dùng giúp tránh dựa vào hoisting. Nếu bạn thấy undefined bất ngờ từ var, hãy kiểm tra xem biến có bị truy cập trước dòng gán hay không.