Hoisting là một trong những khái niệm quan trọng mà bất kỳ nhà phát triển nào sử dụng JavaScript hoặc các framework liên quan đều nên nắm vững. Bạn có bao giờ thắc mắc tại sao có thể gọi hàm hoặc truy cập biến trước khi chúng được khai báo chưa? Nếu có, chào mừng bạn đến với thế giới của JavaScript Hoisting!
Hoisting là gì?
Đây là một trong những câu hỏi phổ biến và câu trả lời thường thấy là:
“Tất cả các khai báo biến và hàm sẽ được đưa lên đầu.”
Tuy nhiên, đây là một hiểu lầm phổ biến. Trong JavaScript, không có gì thực sự được “di chuyển” lên đầu cả. Hãy cùng tìm hiểu chi tiết hơn!
Cách mã JavaScript được thực thi
JavaScript Engine thực thi mã của chúng ta qua hai giai đoạn:
- Giai đoạn khởi tạo (Creation Phase): Engine quét qua mã và cấp phát bộ nhớ cho các khai báo biến và hàm.
- Giai đoạn thực thi (Execution Phase): Engine thực thi mã theo từng dòng, thực hiện các phép gán giá trị và gọi hàm.
Hoisting với biến
Biến là một phần quan trọng trong mọi ngôn ngữ lập trình. Hiểu rõ cách khai báo và sử dụng biến giúp chúng ta tránh được các lỗi không mong muốn.
Ví dụ:
console.log('myName', myName);
var myName = 'JavaScript';
console.log('myName', myName);
Khi chạy đoạn mã trên:
- Dòng đầu tiên in ra
undefined
. - Dòng thứ hai in ra
JavaScript
.
Giải thích:
JavaScript tuân theo một vòng đời biến như sau:
- Khai báo (Declaration):
var myName;
- Khởi tạo (Initialization):
myName = 'JavaScript';
- Sử dụng (Utilization):
console.log('myName', myName);
Trong giai đoạn khởi tạo, biến myName
được khai báo với giá trị mặc định là undefined
. Đến giai đoạn thực thi, giá trị thực sự được gán vào biến.
Hiểu lầm về var
, let
và const
Từ ES6, biến trong JavaScript có thể được khai báo bằng var
, let
hoặc const
.
Hiểu lầm: Chỉ var
được hoisting, còn let
và const
thì không.
Thực tế: Tất cả biến đều được hoisting, nhưng cách chúng hoạt động trong vùng nhớ khác nhau:
var
: Hoisting trong global scope.let
vàconst
: Hoisting trong block scope (kèm theo Temporal Dead Zone).
Ví dụ:
console.log(myName); // ReferenceError
let myName = 'JavaScript';
Đoạn mã trên báo lỗi vì biến myName
nằm trong Temporal Dead Zone (TDZ) – khoảng thời gian từ khi biến được khai báo (bằng let
hoặc const
) đến khi nó được khởi tạo.
Hoisting với function
Hàm là một trong những khối xây dựng cơ bản của JavaScript. Có nhiều cách khai báo hàm, nhưng phổ biến nhất là:
- Function Declaration
- Function Expression
- Arrow Function
Function Declaration:
greetings();
function greetings() {
console.log('Hello from dev community');
}
Đoạn mã trên không báo lỗi vì hàm được hoisting đầy đủ.
Function Expression:
greetings(); // TypeError: greetings is not a function
var greetings = function () {
console.log('Hello from function expression');
};
Trong trường hợp này, chỉ có khai báo biến được hoisting, với giá trị mặc định là undefined
.
Arrow Function:
greetings(); // TypeError: greetings is not a function
const greetings = () => {
console.log('Hello from arrow functions');
};
Arrow function hoạt động tương tự Function Expression khi hoisting.
Thứ tự ưu tiên
Trong JavaScript, Function Declarations có độ ưu tiên cao hơn so với Variable Declarations.
Ví dụ:
var abc;
function abc() {}
console.log(typeof abc); // "function"
Kết luận
- Hoisting là quá trình cấp phát bộ nhớ cho các biến và hàm trước khi mã được thực thi.
- Chỉ có khai báo biến và hàm được hoisting.
const
vàlet
được hoisting nhưng không thể truy cập trước khi khởi tạo.- Function Declarations được ưu tiên hơn Variable Declarations.
- Để tránh lỗi hoisting, hãy luôn khai báo biến và hàm trước khi sử dụng.
Hy vọng bài viết này đã giúp bạn hiểu rõ hơn về hoisting trong JavaScript. Hãy thực hành nhiều trường hợp khác nhau để nắm vững khái niệm này nhé!
Tham khảo: https://developer.mozilla.org/en-US/docs/Glossary/Hoisting