Skip to content

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

最后,constlet声明也被提升,但是它们不会被初始化为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!'

通常情况下,constlet在许多方面提供了更无忧的体验,这也不例外。在访问使用var声明但尚未初始化的变量时,会静默失败,而对于constlet,则会产生清晰、易于调试的错误。

最佳实践

  • 在使用之前,始终先定义变量、函数、对象和类。ESLint可能可以帮助您完成这一点。
  • 如果您的环境/团队允许,优先使用constlet,以减少问题。
  • 如果可能,只使用箭头函数或function声明。一致性有助于减少混淆。
  • 在文件开头使用'use strict'来防止变量提升并强制执行更严格的代码规则。