考虑三种情况,其中a
和k
都未定义:
if (a) console.log(1); // ReferenceError
和
var a = k || "value"; // ReferenceError
看似合理,但是......
var a = a || "value"; // "value"
为什么最后一个案例没有抛出ReferenceError
?是否a
在定义之前被引用?
答案 0 :(得分:11)
这是因为var
的一个名为hoisting的“功能”。根据链接:
因为在执行任何代码之前处理变量声明(以及一般的声明),所以在代码中的任何地方声明变量等同于在顶部声明它。这也意味着变量可以在声明之前使用。此行为称为“提升”,,因为看起来变量声明已移至函数或全局代码的顶部。(强调我的)
所以,例如:
console.log(a);
var a = "foo";
而不是按照您的预期抛出ReferenceError
,因为a
在定义之前被引用,它会记录undefined
。这是因为,如前所述,声明首先处理,基本上发生在顶部,这意味着它与:
var a;
console.log(a);
a = "foo";
前面提到的功能也是如此:
function foo() {
console.log(a);
var a = "foo";
}
这与:
相同function foo() {
var a;
console.log(a);
a = "foo";
}
要了解原因,请查看ECMAScript 2015 Language Specification:
13.3.2变量声明
请注意
var
语句声明范围为the running execution context’s VariableEnvironment的变量。 Var变量在实例化其包含Lexical Environment时创建,并在创建时初始化为undefined
。[...]
由
VariableDeclaration
定义的带有Initializer
的变量被赋予其 {{1}的值执行Initializer
时 {em>AssignmentExpression
,而不是创建变量的时候。(强调我的)
从这里,我们可以收集VariableDeclaration
声明在任何代码执行之前(在它们的词汇环境中)创建并且限定为包含 var
,是在全局范围内,还是在函数中。它们最初的值为VariableEnvironment
。下一部分解释了赋值undefined
的值是执行声明时右侧的值,而不是创建变量时的值。
这适用于您的情况,因为在您的示例中,var
被引用,就像在示例中一样。使用前面的信息,您的代码:
a
可以改写为:
var a = a || "value";
请记住,在执行任何代码之前都会处理所有声明。 JavaScript引擎看到有一个声明变量var a;
a = a || "value";
,并在当前函数或全局范围的顶部声明它。然后给它值a
。由于undefined
是假的,undefined
被分配给a
。
相反,您的第二个示例会抛出value
:
ReferenceError
它也可以改写为:
var a = k || "value";
现在你看到了问题。由于var a;
a = k || "value";
永远不会在任何地方声明,因此不存在具有该标识符的变量。这意味着,与第一个示例中的k
不同,a
永远不会被声明并抛出k
,因为它在声明之前被引用。
但你怎么解释
ReferenceError
?
再次从ES2015语言规范:
在任何VariableEnvironment的范围内,常见的
var a = "123"; var a = a || "124"; // a = "123"
可能出现在多个BindingIdentifier
中,但这些声明集体定义只有一个变量。
说白了:变量可以在同一函数或全局范围内多次定义,但始终定义相同的变量。因此,在您的示例中:
VariableDeclaration
可以改写为:
var a = "123";
var a = a || "124";
再次在同一功能或全局范围内声明var a;
a = "123";
a = a || "124";
仅集体声明一次。 a
被分配到a
,然后再次分配给"123"
,因为"123"
是真实的。
从ES2015开始,在我看来,您应该不再使用"123"
。它具有功能范围,可能会导致意外的分配,如问题中提到的那些。相反,如果您仍想要可变性,请尝试使用var
:
let
这将抛出let a = a || "value";
。即使所有变量都被提升,无论您使用哪个声明者(ReferenceError
,var
或let
),const
和let
都是无效的引用未初始化的变量。此外,const
和let
具有阻止范围,而非功能范围。对于其他语言,这一点更为明确和正常:
const
对战:
function foo() {
{
var a = 3;
}
console.log(a); //logs 3
}
答案 1 :(得分:0)
var a = k || 'value';
var a = a || 'value';
a在你调用var a
时声明但是k不是,这就是为什么第一行是ReferenceError而第二行是'value'
答案 2 :(得分:-1)
if (a) console.log(1); // ReferenceError
在这种情况下,我认为毫无疑问,因为在使用它之前没有声明任何地方,所以参考错误
var a = k || "value"; //ReferenceError
同样的情况这里没有声明k,我们正在尝试使用它
var a = a || "value"; //"value"
现在在这种情况下我们尝试使用a(a in r.h.s)时间a已经在使用之前声明,因此没有错误
如果您想知道参考错误和未定义
之间的区别,请添加此项引用错误-indicate变量尚未声明,而
undefined - 一旦声明为undefined,它就是在分配给任何变量的javascript中的特殊值,表示该变量已声明但未获取任何值