JavaScript中的变量提升是什么?
在执行JavaScript代码之前,首先会对其进行解析和编译(即时编译/JIT)。在编译阶段,变量和函数声明会被放入内存中,这就是所谓的变量提升。
需要注意的是,只有声明会被提升,而初始化不会。这意味着如果你在使用变量之后声明和初始化它,它的值将不会被初始化。然而,这只是一个简化的解释,让我们来看看各种情况:
函数
使用function
声明时,函数可以在定义之前调用,并且会按预期工作。例如:
hello(); // 输出 'Hello world!'
function hello() {
console.log('Hello world!');
}
hello(); // 输出 'Hello world!'
在上面的例子中,function
声明被提升到其作用域的顶部,并且由于函数声明的特性,在声明之前就可以使用。然而,这是唯一的一种行为方式。
var
另一方面,var
声明的行为不同,在初始化之前访问时返回undefined
。例如:
console.log(x); // 输出 'undefined'
f(); // 抛出 'Uncaught TypeError: f is not a function'
var x = 1;
var f = () => 'Hi!';
console.log(x); // 输出 '1'
f(); // 返回 'Hi!'
如你在这个例子中所看到的,var
声明被提升到其作用域的顶部,但是它们的值直到初始化它们的代码执行时才被初始化,因此在此之前它们的值为undefined
。
const 和 let
最后,const
和let
声明也被提升,但是它们不会被初始化为undefined
。相反,它们会给出一个错误,这也是class
声明的行为。例如:
console.log(y); // 抛出 'Uncaught ReferenceError: Cannot access "y" before initialization'
g(); // 抛出 'Uncaught ReferenceError: Cannot access "g" before initialization'
let y = 2;
const g = () => 'Hey!';
console.log(y); // 输出 '2'
f(); // 返回 'Hey!'
通常情况下,const
和let
在许多方面提供了更无忧的体验,这也不例外。在访问使用var
声明但尚未初始化的变量时,会静默失败,而对于const
或let
,则会产生清晰、易于调试的错误。
最佳实践
- 在使用之前,始终先定义变量、函数、对象和类。ESLint可能可以帮助您完成这一点。
- 如果您的环境/团队允许,优先使用
const
和let
,以减少问题。 - 如果可能,只使用箭头函数或
function
声明。一致性有助于减少混淆。 - 在文件开头使用
'use strict'
来防止变量提升并强制执行更严格的代码规则。