App下載

什么是提升及其在 JavaScript 中的工作原理?

一米五的小可愛 2021-09-01 16:10:03 瀏覽數(shù) (2878)
反饋

提升(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ī)則。


0 人點贊