js进阶——什么是提升
JavaScript 中的提升(Hoisting)是一个重要的概念,它指的是 JavaScript 引擎在代码执行之前,会将变量和函数的声明提升到它们所在作用域的顶部。这意味着,即使变量或函数在代码中后面声明,它们的引用可以在声明之前进行。不过,不同类型的声明(如 var
、let
、const
和函数声明)在提升中的行为有所不同。
1. 变量提升(Variable Hoisting)
- 使用
var
声明的变量:在函数或全局作用域内,变量声明会被提升到作用域顶部。但值的初始化并不会被提升,因此在变量声明之前访问该变量会得到undefined
。这是因为只有声明部分被提升,赋值部分保留在原来的位置。
示例:
console.log(x); // undefined
var x = 5;
console.log(x); // 5
上述代码在内部被处理为:
var x;
console.log(x); // undefined
x = 5;
console.log(x); // 5
注意:变量 x
在第一次 console.log
输出时已经被声明了,但还没有赋值,所以返回 undefined
。
- 使用
let
和const
声明的变量:虽然它们的声明也会被提升,但它们在声明之前不可访问。访问时会抛出ReferenceError
,这段不能访问的时间区间被称为暂时性死区(Temporal Dead Zone, TDZ)。
示例:
console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 10;
解释:y
在代码执行过程中是存在的,但在到达 let
声明之前,它位于 TDZ 中,因此访问会抛出错误。
2. 函数提升(Function Hoisting)
- 函数声明(Function Declaration):完整的函数声明会被提升到其作用域的顶部。这意味着即使函数调用出现在函数声明之前,JavaScript 依然能够正确执行代码。
示例:
sayHello(); // "Hello, world!"function sayHello() {console.log("Hello, world!");
}
上述代码在内部被处理为:
function sayHello() {console.log("Hello, world!");
}sayHello(); // "Hello, world!"
- 函数表达式(Function Expression):与变量提升类似,函数表达式中的变量会被提升,但实际的函数定义不会被提升。这意味着,如果在定义之前调用函数表达式,访问到的是
undefined
,而非函数。
示例:
sayHello(); // TypeError: sayHello is not a functionvar sayHello = function() {console.log("Hello, world!");
};
解释:sayHello
被提升为一个未定义的变量,因此在调用时返回 undefined
,而不是一个函数,这会导致 TypeError
。
3. 类提升(Class Hoisting)
- 类声明(Class Declaration):与
let
和const
类似,类声明也会被提升,但同样会存在暂时性死区,在声明之前访问类会抛出ReferenceError
。
示例:
let instance = new MyClass(); // ReferenceError: Cannot access 'MyClass' before initializationclass MyClass {constructor() {this.name = "Example";}
}
解释:虽然类声明被提升,但在其实际定义之前无法使用。
4. 提升的执行顺序
提升的顺序基于作用域的类型和声明的种类:
- 在全局作用域中,
var
声明的变量和函数声明会被提升到全局的最顶端。 - 函数内部的变量和函数会被提升到函数作用域的顶部。
let
、const
、类声明会在作用域中被提升,但不可在提升前访问(即它们会被放入 TDZ 中)。
5. 提升带来的常见问题
提升有时会导致代码行为不符合直觉,特别是对于新手来说,可能会导致难以理解的错误:
undefined
错误:因为var
提升,未初始化的变量会返回undefined
而不是抛出错误。- 函数与变量同名:当变量名和函数名相同时,变量声明会覆盖函数声明。
示例:
console.log(foo); // ƒ foo() {}var foo = 10;function foo() {console.log("I am a function");
}console.log(foo); // 10
在这里,foo
首先被提升为函数声明,所以第一次 console.log
输出函数定义。但由于 var foo = 10
声明的变量在作用域中覆盖了函数,第二次输出的是数值 10
。
6. 小结
- 变量提升:
var
声明的变量会被提升到作用域顶部,但在初始化之前值为undefined
。let
和const
也会被提升,但会产生暂时性死区(TDZ),使得在声明前访问它们会抛出错误。 - 函数提升:函数声明会完整提升,而函数表达式的变量会提升但值不会提升。
- 类提升:类声明也会提升,但会像
let
和const
一样进入暂时性死区。
掌握提升机制有助于理解 JavaScript 的执行流程,写出更清晰的代码。