var a = 0;
if (true) {
console.log(a)
a = 1;
function a() {}
a = 21
console.log(a)
}
console.log(a)
在我看来,由于函数声明的提升,a = 1
和a = 21
会更改局部函数变量,因此in块中将输出21,而outside为0,但真正的结果是在output 1外。
使用chrome调试,结果是这样
在function a() {}
上运行时,它将更改局部变量和全局变量。太奇怪了?
谁能给我一些解释?
答案 0 :(得分:12)
观察到的行为是非严格模式所特有的,并且Firefox执行相同的操作。
之所以采用这种方式,是因为它遵循规范Annex B 3.3中所述的Web兼容性语义。
细节非常复杂,但这是此部分引擎的实现者的实现:
当块中存在内部函数
a
时,处于草率模式,并且当 Web兼容性语义适用(在 规范),然后:
- 内部函数
a
通过类似let
的块作用域(“(let) a
”)被提升- 同时,在包含以下内容的范围中创建了一个变量,其名称也为
a
,但语义为var
(即函数作用域) 块(“(var) a
”)- 当到达声明内部函数的行时,
(let) a
的当前值将被复制到(var) a
(!!)- 从块内部对名称
a
的后续引用将引用(let) a
因此,对于以下情况:
1: var a = 0
2: if(true) {
3: a = 1
4: function a() {}
5: a = 2
6: }
7: console.log(a)
...这是发生了什么
第1行:在外部作用域的变量环境中添加了(var) a
,并为其分配了0
第2行:创建了一个if块,将(let) a
(即function a() {}
)提升到块的顶部,使(var) a
阴影< / p>
第3行: 1
被分配给(let) a
(注意,不是(var) a
)
第4行:(let) a
的值被复制到(var) a
中,因此(var) a
变为1
第5行: 2
被分配给(let) a
(注意,不是(var) a
)
第7行: (var) a
被打印到控制台(因此1
被打印)
Web兼容性语义是一些行为的集合,这些行为试图对后备语义进行编码,以使现代浏览器能够尽可能多地与Web上的旧代码保持向后兼容性。这意味着它将对规范外部实施的行为进行编码,并由不同的供应商独立进行。在非严格模式下,由于浏览器供应商“走自己的路”的历史,几乎可以预期会有奇怪的行为。
但是,请注意,规范中定义的行为可能是错误沟通的结果。 Twitter上的Allen Wirfs-Brock said:
无论如何,根据我想像的任何合理解释,最后报告的a == 1的结果都不正确。
... and:
应该为
function a(){}
!因为在块内,对a
的所有显式引用都是对块级绑定的。只有隐式的B.3.3分配应该转到外部的a
最后的注释:Safari给出了完全不同的结果({0
由第一个console.log
打印,21
由后一个console.log
打印)。据我了解,这是因为Safari尚未对Web兼容性语义(example)进行编码。
故事的道德?使用严格模式。
答案 1 :(得分:0)
这也是“有趣的”。似乎a = 21
将a
分配给了全局变量,并且函数引用不可访问。
var a = 0;
if (true) {
function a() {}
console.log(typeof a, a) // number 0
a = 21;
}
console.log(typeof a, a) // number 21
答案 2 :(得分:-1)
a = 1;
重新分配给原始变量,使其值为1
。 function a() {}
仅在if条件下屏蔽a
并“接管” a
。详细了解shadowing and masking。
答案 3 :(得分:-2)
嗨。
让我们以这种方式看一下,当您声明一个名为'a = 1'的变量(声明的变量是全局变量)时,会将一个内存房子分配给 变量 具有以下值:1
在声明函数之后:
function a() {}
不再有'a'变量类型的存储空间,并且a是一个函数。
但是,'a'的存储空间没有清除,仍然可用,就像一个被调用一次并返回最后一个赋值的函数一样!
因此,当您编写以下代码时:
a = 1;
function a() {}
它就像:
function a(){
return 1;
}
console.log(a());
这个问题就像是由于不完整的性质(我的意思是记忆库类型)改变一样。