提升(Hoisting)是 JavaScript 的概念,它使 JavaScript 與 Java 語言不同。在 Java 中,代碼中創(chuàng)建的每個變量都具有塊級作用域。意味著如果我們創(chuàng)建了任何將其可見性限制在聲明它的塊中的變量。因此,如果我們在聲明中使用上面的變量,則會出錯。但是在 JavaScript 中,變量可以在聲明之前使用,這種機制稱為 Hoisted。這是 JavaScript 的默認(rèn)行為。
提升是 JS 的默認(rèn)行為,即在代碼執(zhí)行之前定義作用域頂部的所有聲明。提升的好處之一是它使我們能夠在函數(shù)出現(xiàn)在代碼中之前調(diào)用它們。JavaScript 只提升聲明,而不是初始化。
理解 JavaScript 提升變量究竟是什么聲明和初始化按以下順序進行:
聲明 –> 初始化/賦值 –> 用法
// Variable lifecycle
let x; // Declaration
x = “hoisting”; // Assignment
console.log(x); // Usage
最重要的是,應(yīng)該始終記住 JavaScript 首先在后臺聲明變量。然后,初始化它們。因此,了解變量聲明的處理發(fā)生在任何代碼執(zhí)行之前也是很好的。但是,在執(zhí)行分配它們的代碼之前,JavaScript 中不存在未聲明的變量。因此,在執(zhí)行賦值時,分配給未聲明變量的值會隱式地將其創(chuàng)建為全局變量。這指定所有未聲明的變量都是全局變量。
// hoisting
function Hoisting(){
x = 100;
let y = 200;
}
Hoisting();
console.log(x); // 100
console.log(y); // Reference Error: y is not defined
在上面的代碼示例中,有一個名為 Hoisting() 的函數(shù)。因此,我們有一個沒有使用 let/var/const 聲明的變量和一個 let 變量 y。將未聲明的變量分配給全局作用域是由 JavaScript 完成的。但是對于變量 y,我們得到了一個引用錯誤(?Refence Error
?)。
在函數(shù)作用域變量中托管
在 ES5 中,我們考慮 var 關(guān)鍵字。與 let/const 相比,使用 var 進行提升有些不同。使用 var 來查看提升如何工作的示例:
var num (global)
console.log(car); // undefined
var car = ‘Lamborgini’;
在上面的代碼中,當(dāng)記錄在使用它之后聲明和分配的變量名稱時,編譯器給出“undefined”的結(jié)果。這是意料之中的,因為我們應(yīng)該在聲明它之前就嘗試使用 ?car
? 變量,因為我們應(yīng)該得到 ReferenceError。但是解釋器對此有不同的看法,如下所示:
//how interpreter sees the above code
var car;
console.log(car); // undefined
car = ‘Lamborgini’;
let 和 const 關(guān)鍵字。
用 let 或 const 聲明的變量和常量不會被提升!
JavaScript 初始化不是初始化。
JavaScript 只能提升聲明,而不能用來初始化。
var a = “volkswagon”; // Initialize a
var b = “Lamborgini”; // Initialize b
elem = document.getElementById("car"); // Find an element
elem.innerHTML = a + " " + b; // Display a and b as volkswagon and lamborgini
在上面的代碼中,因為變量的聲明發(fā)生在結(jié)果之前。所以結(jié)果,代碼的執(zhí)行打印了變量 a 和 b 的結(jié)果。
var a = “i10”; // Initialize a
elem = document.getElementById("car"); // Find an element
elem.innerHTML = "a is " + a + “ and b is " + b; // Display a and b
var b = “Lamborgini”; // Initialize b
結(jié)果:
a 是 ?i10
?而 b 是未定義的。
因此,這是因為只發(fā)生了聲明(?var b
?)的提升,而不是初始化(?=“Lamborgini”
?)到頂部。由于提升,b 在使用之前已經(jīng)聲明,但是因為初始化沒有提升,所以 b 的值是未定義的。
提升類(Hoisting Classes)
JavaScript 類可以分為兩類:
- 類聲明
- 類表達式
在類聲明中,它們很像函數(shù)對應(yīng)物。因此,這意味著不會提升 JavaScript 類聲明。但是,它們在評估之前保持未初始化狀態(tài)。因此,這實際上意味著必須先聲明一個類,然后才能使用它。
var car1 = new car();
car1.height = 5;
car1.weight = 500;
console.log(car1); // Output: ReferenceError: car is not defined
class car{
constructor(height, weight) {
this.height = height;
this.weight = weight;
}
}
在上面的代碼中,出現(xiàn)了引用錯誤。這是因為在 ?car1
?變量初始化之后,汽車類的定義就發(fā)生了。為了解決這個錯誤,我們只需要在 ?car1
?初始化之前定義 ?car
?類。這是在類聲明中托管。
class car{
constructor(height, weight) {
this.height = height;
this.weight = weight;
}
}
var car1 = new car();
car1.height = 5;
car1.weight = 500;
console.log(car1);
因此,這給出了正確的結(jié)果。在 Class 表達式中它們很像它們的函數(shù)對應(yīng)物。因此,這意味著沒有提升類表達。因此,下面是類表達式的未命名或匿名變體的示例:
var rect = new shapes();
rect.height = 10;
rect.width = 20;
console.log(rect); // Output: TypeError: shapes is not a constructor
var shapes = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
Thus, the correct way to do it is like this:
var shapes = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
var rect = new shapes();
rect.height = 10;
rect.width = 20;
console.log(rect);
總結(jié)
對于許多開發(fā)人員來說,提升是 JavaScript 的一種未知行為。許多開發(fā)人員也忽略了它的重要性。此外,如果開發(fā)人員不了解提升,程序可能包含 bug(錯誤)。為了避免 bug,請始終在每個作用域的開頭聲明所有變量。因此,這就是 JavaScript 解釋代碼的方式,這始終是一個很好的規(guī)則。