在节点环境中运行下面的代码。在浏览器控制台中运行它不允许重新声明变量的变量。
console.log(a);
var a = 5;
根据吊装,上面的代码将看起来像这样
var a = undefined;
console.log(a); // undefined
a = 5;
a
变量被提升到文件的顶部。JS 引擎在执行之前为此变量分配内存。问题是为什么下面的代码安慰5
而不是未定义。
var a = 5;
console.log(a);
var a = 6;
我正在查看此代码并想象它看起来像这样:
var a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
我想确定答案而不是猜测。JS引擎足够聪明,可以看到a
变量已经被声明,并且在这种情况下会忽略下一个var表达式并重新提升?所以输出应该看起来像:
var a = 5;
console.log(a); // 5
a = 6;
所以就像:
- JS引擎第一次看到
a
变量的声明(在本例中与初始化一起),因此它正在分配内存。 - JS引擎第二次看到
a
变量的声明,但将忽略提升,因为给定名称的变量已经在内存中。
我错了吗?
前言:在现代 JavaScript 中,永远不应该使用var
。使用let
或const
。
JavaScript 引擎分两步处理var
:
-
进入全局作用域或函数作用域后,它会处理整个作用域中的每个
var
,为它们定义初始化的变量,并undefined
值。如果一个变量被声明了不止一次var
,就好像它被声明了一次一样。 -
然后,它开始逐步执行代码。在该分步执行中,
var
语句(var a = 5
中的= 5
)上的任何初始值设定项都被视为赋值。因此,在这一点上var a = 5
待遇与a = 5
完全相同。
所以在你的例子中:
var a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
就好像你写了这个:
var a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
或者这个:
a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
或者这个:
a = 5;
a = undefined;
console.log(a); // undefined
var a = 6;
或者这个:
var a;
a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
或者这个:
a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
var a;
甚至这个(但请不要! :-) ):
var a = 5;
var a = undefined;
console.log(a); // undefined
var a = 6;
var a;
也就是说,首先创建所有用var
声明的变量(并且只创建一次),然后代码运行,就好像它们上的任何初始值设定项都是赋值一样。
这不是处理let
、const
和class
声明的方式(统称为:词法范围的声明)。首先:同一范围内的多个声明是错误(包括其中一个声明与var
,另一个声明与词法范围的声明之一)。第二:它们被提升(在上面的步骤 1 中),但提升的绑定¹ 是未初始化的,直到在分步代码中执行声明,此时它被初始化(使用初始值设定项中的值,或者使用undefined
如果只是例如let a;
)。从进入范围到初始化绑定点之间的时间称为临时死区。var
没有它var
因为变量在创建时被初始化(值为undefined
),但let
、const
和class
声明可以。
¹ 术语绑定是类变量事物的总称。在代码中:
function example(p) {
var v;
let l;
const c = 42;
function f() {
}
class C {}
}
进入example
函数的作用域时创建的绑定包括p
(参数)、v
(var
变量)、l
(let
变量)、c
(const
常量)、f
(函数声明创建的绑定)和C
(class
声明创建的绑定)。(注意:函数表达式和class
表达式的处理方式略有不同。