Wil*_*niz 4 javascript reference language-lawyer
考虑三种情况,其中两个a
和k
是不确定的:
if (a) console.log(1); // ReferenceError
Run Code Online (Sandbox Code Playgroud)
和
var a = k || "value"; // ReferenceError
Run Code Online (Sandbox Code Playgroud)
似乎合理,但......
var a = a || "value"; // "value"
Run Code Online (Sandbox Code Playgroud)
为什么最后一个案子不扔ReferenceError
?a
在定义之前没有被引用?
Li3*_*357 11
这是因为其中一个var
称为" 吊装 "的"特征" .根据链接:
因为在执行任何代码之前处理变量声明(以及一般的声明),所以在代码中的任何地方声明变量等同于在顶部声明它.这也意味着变量可以在声明之前使用.此行为称为"提升",因为看起来变量声明被移动到函数或全局代码的顶部.(强调我的)
所以,例如:
console.log(a);
var a = "foo";
Run Code Online (Sandbox Code Playgroud)
而不是ReferenceError
按照您的预期抛出,因为a
它在定义之前被引用,它会记录undefined
.这是因为,如前所述,声明首先处理,基本上发生在顶部,这意味着它与以下内容相同:
var a;
console.log(a);
a = "foo";
Run Code Online (Sandbox Code Playgroud)
对于前面提到的功能也是如此:
function foo() {
console.log(a);
var a = "foo";
}
Run Code Online (Sandbox Code Playgroud)
这与以下相同:
function foo() {
var a;
console.log(a);
a = "foo";
}
Run Code Online (Sandbox Code Playgroud)
要了解原因,请查看ECMAScript 2015语言规范:
13.3.2变量声明
注意
一个
var
声明宣称的作用域变量运行执行上下文的VariableEnvironment.Var变量在实例化包含词法环境undefined
时创建,并在创建时初始化.[...]
由定义的变量
VariableDeclaration
用Initializer
被分配了其值Initializer
的AssignmentExpression
所述时VariableDeclaration
被执行,而不是在创建变量时.(强调我的)
从这里,我们可以收集var
声明是在任何代码执行之前(在它们的词法环境中)创建的,并且范围限定为包含VariableEnvironment
,它可以在全局范围内,也可以在函数中.它们最初被赋予价值undefined
.下一部分解释了var
赋值的值是执行声明时右侧的值,而不是创建变量时的值.
这适用于您的情况,因为在您的示例中,a
引用就像在示例中一样.使用前面的信息,您的代码:
var a = a || "value";
Run Code Online (Sandbox Code Playgroud)
可以改写为:
var a;
a = a || "value";
Run Code Online (Sandbox Code Playgroud)
请记住,在执行任何代码之前都会处理所有声明.JavaScript引擎看到有一个声明,变量a
并在当前函数或全局范围的顶部声明它.然后给出它的价值undefined
.由于undefined
是假的,a
被分配给value
.
相比之下,你的第二个例子抛出ReferenceError
:
var a = k || "value";
Run Code Online (Sandbox Code Playgroud)
它也可以改写为:
var a;
a = k || "value";
Run Code Online (Sandbox Code Playgroud)
现在你看到了问题.由于k
从不在任何地方声明,因此不存在具有该标识符的变量.这意味着,与a
第一个示例中的不同,k
永远不会声明并抛出ReferenceError
它,因为它在声明之前被引用.
但你怎么解释
var a = "123"; var a = a || "124"; // a = "123"
?
再次从ES2015语言规范:
在任何VariableEnvironment的范围内,公共
BindingIdentifier
可能出现在多个变量环境中,VariableDeclaration
但这些声明集合仅定义一个变量.
说白了:变量可以在同一个函数或全局范围内多次定义,但始终定义相同的变量.因此,在您的示例中:
var a = "123";
var a = a || "124";
Run Code Online (Sandbox Code Playgroud)
它可以改写为:
var a;
a = "123";
a = a || "124";
Run Code Online (Sandbox Code Playgroud)
声明a
在同一个函数或全球范围内再次集体宣布只有一次.a
分配给"123"
,然后"123"
再次分配,因为"123"
是真的.
从ES2015开始,在我看来,你应该不再使用了var
.它具有功能范围,可能会导致意外的分配,如问题中提到的那些.相反,如果您仍想要可变性,请尝试使用let
:
let a = a || "value";
Run Code Online (Sandbox Code Playgroud)
这将抛出一个ReferenceError
.即使所有的变量都悬挂,无论哪个声明符使用(var
,let
,或const
),与let
和const
它是无效引用一个未初始化的变量.此外,let
和const
有块范围,不发挥作用的范围.对于其他语言,这一点更为明确和正常:
function foo() {
{
var a = 3;
}
console.log(a); //logs 3
}
Run Code Online (Sandbox Code Playgroud)
与:
function foo() {
{
let a = 3;
}
console.log(a); //ReferenceError
}
Run Code Online (Sandbox Code Playgroud)