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

2021-09-01 16:10:03 浏览数 (3)

提升(Hoisting)是 JavaScript 的概念,它使 JavaScript 与 Java 语言不同。在 Java 中,代码中创建的每个变量都具有块级作用域。意味着如果我们创建了任何将其可见性限制在声明它的块中的变量。因此,如果我们在声明中使用上面的变量,则会出错。但是在 JavaScript 中,变量可以在声明之前使用,这种机制称为 Hoisted。这是 JavaScript 的默认行为。

提升是 JS 的默认行为,即在代码执行之前定义作用域顶部的所有声明。提升的好处之一是它使我们能够在函数出现在代码中之前调用它们。JavaScript 只提升声明,而不是初始化。

理解 JavaScript 提升变量究竟是什么声明和初始化按以下顺序进行:

声明 –> 初始化/赋值 –> 用法

// Variable lifecycle
let x;                 // Declaration
x = “hoisting”;        // Assignment
console.log(x);        // Usage

最重要的是,应该始终记住 JavaScript 首先在后台声明变量。然后,初始化它们。因此,了解变量声明的处理发生在任何代码执行之前也是很好的。但是,在执行分配它们的代码之前,JavaScript 中不存在未声明的变量。因此,在执行赋值时,分配给未声明变量的值会隐式地将其创建为全局变量。这指定所有未声明的变量都是全局变量。

// hoisting
function Hoisting(){
  x = 100;
  let y = 200;
}
Hoisting();
console.log(x); // 100
console.log(y); // Reference Error: y is not defined

在上面的代码示例中,有一个名为 Hoisting() 的函数。因此,我们有一个没有使用 let/var/const 声明的变量和一个 let 变量 y。将未声明的变量分配给全局作用域是由 JavaScript 完成的。但是对于变量 y,我们得到了一个引用错误(​Refence Error​)。

在函数作用域变量中托管

在 ES5 中,我们考虑 var 关键字。与 let/const 相比,使用 var 进行提升有些不同。使用 var 来查看提升如何工作的示例:

var num (global)
console.log(car);    // undefined
var car = ‘Lamborgini’;

在上面的代码中,当记录在使用它之后声明和分配的变量名称时,编译器给出“undefined”的结果。这是意料之中的,因为我们应该在声明它之前就尝试使用 ​car​ 变量,因为我们应该得到 ReferenceError。但是解释器对此有不同的看法,如下所示:

//how interpreter sees the above code
var car;
console.log(car); // undefined
car = ‘Lamborgini’;

let 和 const 关键字。

用 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

在上面的代码中,因为变量的声明发生在结果之前。所以结果,代码的执行打印了变量 a 和 b 的结果。

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

结果:

a 是 ​i10 ​而 b 是未定义的。

因此,这是因为只发生了声明(​var b​)的提升,而不是初始化(​=“Lamborgini”​)到顶部。由于提升,b 在使用之前已经声明,但是因为初始化没有提升,所以 b 的值是未定义的。

提升类(Hoisting Classes)

JavaScript 类可以分为两类:

  • 类声明
  • 类表达式

在类声明中,它们很像函数对应物。因此,这意味着不会提升 JavaScript 类声明。但是,它们在评估之前保持未初始化状态。因此,这实际上意味着必须先声明一个类,然后才能使用它。

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;
  }
}

在上面的代码中,出现了引用错误。这是因为在 ​car1 ​变量初始化之后,汽车类的定义就发生了。为了解决这个错误,我们只需要在 ​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);

因此,这给出了正确的结果。在 Class 表达式中它们很像它们的函数对应物。因此,这意味着没有提升类表达。因此,下面是类表达式的未命名或匿名变体的示例:

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);

总结

对于许多开发人员来说,提升是 JavaScript 的一种未知行为。许多开发人员也忽略了它的重要性。此外,如果开发人员不了解提升,程序可能包含 bug(错误)。为了避免 bug,请始终在每个作用域的开头声明所有变量。因此,这就是 JavaScript 解释代码的方式,这始终是一个很好的规则。


0 人点赞