Hoisting là một hành vi mặc định của JavaScript nhằm đưa các khai báo lên đầu phạm vi.
Khi JS engine thông dịch một chương trình nào đó, nó sẽ scan qua hết chương trình rồi mới tiến hành thông dịch. Quá trình này sẽ đưa các khai báo biến hoặc hàm lên đầu phạm vi (global scope, function scope hoặc local scope1).
Về mặt ngữ nghĩa, hoisting mang ý nghĩa là kéo lên hay cẩu lên (đồng nghĩa với pull up).
Declare a Variable with var
Keyword
Từ khóa var
hỗ trợ hoisting, JS Engine khi thông dịch sẽ đưa các câu lệnh khai báo lên trên đầu.
a = 10
var a
Đoạn code trên tương đương với:
var a
a = 10
Tuy nhiên, ta cần chú ý rằng chỉ những khai báo mới được đẩy lên trên, các phép gán không được tính là phép khai báo. Do đó mà trong đoạn code bên dưới ta không thể xuất ra chuỗi “Kwan”.
console.log(name) // undefined
var name = "Kwan"
Nói cách khác, đoạn code trên tương đương:
var name
console.log(name) // undefined
name = "Kwan"
Function Declaration
Khai báo hàm cũng có tính chất hoisting tương tự như khi khai báo bằng var
:
console.log(sum(1, 2)) // 3
function sum(a, b){
return a + b
}
Thậm chí, dù khai báo sau từ khóa return
, hàm vẫn có tính chất hoisting.
const counter = createCounter()
console.log(counter()) // 1
// Hoisting
function createCounter(){
let count = 0
return increase
// Hoisting
function increase(){
return ++count
}
}
Một ví dụ khác:
var tip = 100;
(function() {
console.log("I have $" + husband()) // "I have NaN"
function wife() {
return tip * 2
}
function husband() {
return wife() / 2
}
var tip = 10
})()
Ở đoạn code trên, khai báo biến tip
bên trong hàm sẽ được hoist và đưa lên đầu, hai hàm wife
và husband
cũng tương tự. Tuy nhiên, chỉ có khai báo được đưa lên đầu phạm vi, còn phép gán tip = 10
vẫn giữ nguyên vị trí. Điều này dẫn đến việc biến tip
sẽ có giá trị là undefined
.
Khi tính toán với giá trị undefined
, kết quả trả ra sẽ là NaN
.
Biến tip
ở bên ngoài không được sử dụng do đã tồn tại một biến tip
bên trong self-invoking function.
Declare a Variable with let
and const
Keywords
Important
Tất cả các loại khai báo đều có tính hoisting, kể cả
let
vàconst
.
Tuy nhiên, hai từ khóa let
và const
lại có cách hoisting khác với từ khóa var
và khai báo hàm.
Ví dụ:
{
console.log(country) // Uncaught ReferenceError: : can't access lexical declaration 'country' before initialization
let country = "Việt Nam"
}
Important
Trong đoạn code trên, biến
country
vẫn được hoist, nhưng nó sẽ không được tạo giá trị mặc định (undefined
). Lúc này, nó sẽ nằm trong temporal dead zone, là một vùng chứa các biến không thể truy cập được trước khi các biến này được khởi tạo với một giá trị.
Why Hoisting?
Lý do mà chương trình cần phải thực hiện hoisting là để các đoạn code trong một block nào đó ưu tiên sử dụng các biến được khai báo trong block đó hơn là các biến bên ngoài.
Ví dụ:
let name = "Kwan"
{
let name = "Kuan"
{
console.log(name) // Uncaught ReferenceError: can't access lexical declaration 'country' before initialization
let name = "Quân"
}
}
Dòng lệnh console.log()
sẽ ưu tiên sử dụng biến name
có giá trị là “Quân” trước khi xét đến biến name
ở bên ngoài block scope. Để nhận biết là có sự tồn tại của biến name
trong cùng một block scope, JS sẽ sử dụng hoisting để đưa biến name = "Quân"
lên trên đầu phạm vi.
Tuy nhiên, như đã nói, do khai báo bằng let
, biến name
này sẽ nằm trong temporal dead zone và không thể truy cập được.
Related
table tags as Tags, file.cday as Created
from [[Hoisting]]
sort file.ctime asc