Hoisting
Quá trình chạy chương trình JavaScript được chia thành hai giai đoạn: giai đoạn biên dịch và giai đoạn thực thi.
Trong giai đoạn biên dịch, trình thông dịch JavaScript sẽ thực hiện một công việc, đó là đọc khai báo biến
và xác định phạm vi hoạt động
của chúng.
-
Khai báo biến
- Biến được khai báo bằng từ khóa
var
hoặclet
, nếu chưa gán giá trị, giá trị của biến sẽ làundefined
. - Nếu biến được khai báo bằng từ khóa
const
nhưng không được gán giá trị, sẽ gây ra lỗi.
- Biến được khai báo bằng từ khóa
-
Phạm vi biến
- Biến toàn cục có phạm vi trên toàn bộ chương trình.
- Biến cục bộ chỉ có phạm vi trong hàm và các hàm lồng nhau.
Trong JavaScript, việc sử dụng biến hoặc hàm mà chưa được khai báo sẽ gây ra lỗi.
Hoisting được sinh ra để giải quyết vấn đề này
Khai báo được nâng lên (hoisting) bao gồm khai báo biến được nâng lên và khai báo hàm được nâng lên:
-
Khai báo biến được nâng lên: Biến được khai báo bằng từ khóa
var
,let
vàconst
sẽ được trình thông dịch JavaScript nâng lên đầu phạm vi hiện tại trước khi thực thi mã. -
Khai báo hàm được nâng lên: Hàm được khai báo bằng cách sử dụng khai báo hàm (không phải biểu thức hàm) sẽ được trình thông dịch JavaScript nâng lên đầu phạm vi hiện tại trước khi thực thi mã, và khai báo hàm được nâng lên ưu tiên hơn khai báo biến được nâng lên.
Mã JavaScript được biên dịch trước khi thực thi, trong quá trình biên dịch, trình thông dịch JavaScript sẽ tìm tất cả các khai báo và thiết lập phạm vi cho chúng, do đó, tất cả các khai báo trong phạm vi hiện tại bao gồm cả biến và hàm sẽ được xử lý trước bất kỳ mã nào được thực thi.
Lưu ý rằng chỉ có khai báo được nâng lên, gán giá trị không được nâng lên, khai báo được thực hiện trong giai đoạn biên dịch, trong khi gán giá trị được thực hiện trong giai đoạn thực thi. Điều này có nghĩa là khai báo được nâng lên, trong khi gán giá trị vẫn ở nguyên chỗ, chờ đợi thực thi.
Khai báo biến được nâng lên
Dưới đây là ví dụ về khai báo biến được nâng lên theo chuẩn.
Tương đương với:
Ở đây, chúng ta sử dụng được khai báo biến được nâng lên, trong khi việc gán giá trị vẫn giữ nguyên tại chỗ, chờ đợi thực thi.
Khai báo hàm được nâng lên
Có hai cách tạo hàm trong JavaScript:
- Khai báo hàm
- Biểu thức hàm
🌰 Ví dụ: Khai báo hàm
🌰 Ví dụ: Biểu thức hàm
Giải thích: Dù cả hai ví dụ đều gọi hàm trước khi tạo hàm, kết quả lại khác nhau. Nguyên nhân là khi sử dụng khai báo hàm, khai báo hàm (bao gồm cả định nghĩa) sẽ được nâng lên đến đầu phạm vi, trong khi cách tạo biểu thức chỉ nâng lên biến foo
đến đầu phạm vi và tại thời điểm đó, foo
có giá trị là undefined
, do đó gọi foo
sẽ gây ra lỗi: foo
không phải là một hàm.
Hãy xem ví dụ khác:
Sau khi được nâng lên trong giai đoạn biên dịch, kết quả của đoạn mã trên tương đương với:
Tóm lại:
- Khai báo hàm được nâng lên, đưa cả khai báo và định nghĩa hàm lên đầu phạm vi.
- Khai báo biến được nâng lên, chỉ nâng lên phần khai báo (trạng thái chưa gán giá trị), phần gán giá trị vẫn giữ nguyên tại chỗ.
Ghi đè hàm
Khai báo hàm và khai báo biến đều được nâng lên. Tuy nhiên, khai báo hàm sẽ ghi đè lên khai báo biến.
🌰 Ví dụ:
Tuy nhiên, nếu biến đã được gán giá trị, giá trị cuối cùng sẽ là giá trị của biến.
Việc khai báo biến lặp lại là vô ích, nhưng khai báo hàm lặp lại sẽ ghi đè lên khai báo trước đó (bất kể là khai báo biến hay khai báo hàm).
Khai báo lặp lại vô hiệu
Kết quả là 1, đoạn mã trên tương đương với:
Ưu tiên khai báo hàm
Do khai báo hàm được nâng lên trước khai báo biến, nên khai báo biến sẽ không có hiệu lực.
Ghi đè khai báo hàm
Khai báo hàm sau sẽ ghi đè lên khai báo hàm trước đó.
Vì vậy, tránh khai báo lặp lại trong cùng một phạm vi.