以下内容不一定对,仅个人理解
众所周知,类似var a = 1;这种可分为两步,第一步编译为var a;提升至自身作用域(非块状作用域)最顶端,第二步执行a = 1;而函数也存在提升,如果函数与变量同时存在且同名时。函数优先提升,变量之后提升,由于变量提升时是重复声明,所以会被忽略掉。
var x = 1
function x(){}
console.log(x) //1
//以上代码可看成如下
function x(){}; //函数提升,之后var x;变量提升,但属于重复声明,忽略
x = 1; //执行x = 1
看完上面代码后我觉得我懂了,然鹅面试题跟我说,不,你不会。。。
var a = 0;
if (true) {
a = 10;
console.log(a, window.a); //10 0
function a() {}
console.log(a, window.a); //10 10
a = 20;
console.log(a, window.a); //20 10
}
console.log(a); //10
上面的代码多了个块级作用域。情况就复杂了。
如果按照我之前的简单理解,函数提升至顶部。转换后代码如下:
function a(){}
a = 0;
if(true){
a = 10;
console.log(a, window.a); //10 10
console.log(a, window.a); //10 10
a = 20;
console.log(a, window.a); //20 20
}
console.log(a); //20
//可见以上代码运行结果跟原代码的并不一致
所以函数的提升应该不是简单的提升到顶部。特别是在存在块作用域的情况下
以下就纯属猜想了。
1、函数声明会提升至在其所在函数作用域顶部。但并未把函数体提升过去,仅创建一个函数名的变量标识,内容为undefined
2、函数声明会同时提升至块作用域顶部,创建类似let的块作用域变量(仅块作用域有效)
3、当运行至函数声明位置时,会将1步骤中提升至函数作用域顶部的变量重新赋值为当前值。
如果按以上三条规则,翻译原代码如下
var a; //函数提升至所在非块作用域(当前为全局)顶部
//忽略第二次var a
a = 0; //第一次赋值(原var a = 0;拆分为var a和a=0)
if (true) {
let a = function() {}; //函数提升至块作用域顶部(此处a与外部a不是同一个)
a = 10; //块内赋值
console.log(a, window.a);
window.a = a; //函数所在行执行后会映射至外部(最近的非块作用域)
console.log(a, window.a);
a = 20;
console.log(a, window.a);
}
console.log(a);
按以上规则顺利解决问题。