学生提了一个问题
var a = 100
{
a = 10
function a() {}
a = 20
console.log(a) // 20
}
console.log(a) // 10
最后打印20和10
。这确实是一个奇怪的现象,因为按照以前对函数提升的理解,函数的声明部分会提升到生命周期的最前,赋值会提升到代码块的最前,这个例子显然不符合这个规则,那是怎么回事呢?
stackoverflow上有相同问题,看过答案后,现在我对这段代码的理解是,function的声明在代码块中
的提升会产生两个提升,一个是到生命周期的最前端,一个是到代码块的最前端成为一个局部变量(生命周期是块,类似于let),所以形成内外两个生命周期的变量。而且function提升的位置永远比同var更靠前。
这样上面的代码就变成(a1表示外部生命周期的a,a2表示块作用域中的a)
var a1 <-- function 提升到外部的声明
a1 = 100
{
function a2() {}
a = 10 <-- 解释器解释到这时候a指向a1,所以a1=10
a1 = a2 <-- function留在原地的部分要用a1赋值a2
a = 20 <-- 解释器解释到这的时候a2已经提升了,所以它是a2=20
console.log(a) // 20 <-- 这里生命周期较近的是a2,所以打印20
}
console.log(a) // 10 <-- 这里是a1,打印10
另外当function在块作用域中提升后,效果类似于let,所以还有这样的效果
{
var x = 1
function x() {} // 报错,重复声明
}
还有这样一个效果
{ //block 1
function foo() { // declared in block 1
return 1;
}
console.log("block 1: foo() === 1", foo() === 1);
{ // block 2
function foo() { // declared in block 2
return 2;
}
console.log("block 2: foo() === 2", foo() === 2);
}
console.log("block 1: foo() === 1", foo() === 1);
}
console.log("block 1: foo() === 1", foo() === 1);
/* 结果
block 1: foo() === 1 true
block 2: foo() === 2 true
block 1: foo() === 1 true
block 1: foo() === 1 true
*/
运行到block 2(7到12行位置的function foo),发现全局有一个变量已经叫foo,并且它处于block1处的局部变量foo1控制之下,所以会在此代码块里生成一个局部变量foo2(类似let声明的)而不覆盖全局的foo并且区分开和foo1的生命周期,所以造成这种现象。
如果注释掉2到5行,则全局变量foo也会被赋值为 block2处的foo。
总之,推荐用"use strict"
可以减少这种奇怪的现象。
<script>
"use strict"
var a = 100
{
a = 10
function a() {}
a = 20
console.log(a) // 20
}
console.log(a) // 100
</script>