一道块级作用域的有关的题:
console.log(a, window.a)
var a = 0;
console.log(a, window.a);
if(true){
console.log(a, window.a);
a = 1;
console.log(a, window.a);
function a(){}
console.log(a, window.a);
a = 21;
console.log(a, window.a);
}
console.log(a, window.a);
如上你知道输出是什么?
点击查看答案:
console.log(a,window.a) // undefined undefined
var a = 0;
console.log(a,window.a); // 0 0
if(true){
console.log(a,window.a); // ƒ a(){} 0
a = 1;
console.log(a,window.a); // 1 0
function a(){}
console.log(a,window.a); // 1 1
a = 21;
console.log(a,window.a); // 21 1
}
console.log(a,window.a); // 1 1
是不是结果有些意料之外?
我们一起研究研究,怎么会出现这垃圾情况。
先试试没有function a(){}的时候打印会是什么样子的
console.log(a, window.a); // undefined undefined
var a = 0;
console.log(a, window.a); // 0 0
if(true){
a = 1;
console.log(a, window.a); // 1 1
// function a(){}
a = 21;
console.log(a, window.a); // 21 21
}
console.log(a, window.a); // 21 21
如上可看到,当没有函数的时候就是会一切正常
此时可猜测,是作用域中的function a(){}捣的乱,通过多方查找资料找到的合理的解释是:
var a = 0;
if(true){
a = 1;
console.log(a, window.a); // 1 0
function a(){} //
a = 21;
console.log(a, window.a); // 21 1
}
1.函数声明被提升,并绑定到内部块变量。
意思就是,if {} 中因为查询到有函数的声明,便会把函数提到当前{}的最前面,此时该函数是内部的变量,并没有赋值到全局。
验证一下:
var a = 0
if(true){
被提升的 function a(){} // 被提升到内部块的最上面
// 此时内部块中有a变量,全局中并没有
console.log(a, window.a); // ƒ a(){}, 0
// window.a 为 0 此时全局中的a不会被改变
function a(){} // 真正的函数声明处
}
2.到达函数声明,将块变量复制到外部变量中。
意思就是,到达真正的函数声明处 ,才会把当前被提升的a 赋值给外部变量(全局)
验证一下:
var a = 0
if(true){
被提升的 function a(){} // 被提升到内部块的最上面
console.log(a, window.a); // ƒ a(){}, 0
function a(){} // 真正的函数声明处,执行到这步时会才将变量a挂载在全居中
console.log(a, window.a); // ƒ a(){}, ƒ a(){}
// window.a 为 ƒ a(){} 此时全局中的a才会被改变
}
验证二下:
因为能多次创建同名的函数,所以多次声明a函数,验证是不是只要遇见a的函数声明就会将a 赋值给外部变量
var a = 0;
if(true){
console.log(a, window.a); // ƒ a(){}, 0
a = 1; // 此时a改变的是内部块中的函数的提升
console.log(a, window.a); // 1 0
function a(){} // 遇见a的函数声明,此时将a赋值给外部变量(此时a为1)
console.log(a, window.a); // 1 1
a = 21; // 此时a改变的是内部块中的函数的提升
console.log(a, window.a); // 21 1
function a(){} // 遇见a的函数声明,此时将a赋值给外部变量(此时a为21)
console.log(a, window.a); // 21 21
}
如上,可得出结论: 函数会被提到当前{}的最前面,此时该函数是内部的变量, 等到达真正的函数声明处 ,才会把当前被提升的a 赋值给外部变量。
结合这句话在回过头看一下题,是不是就清晰了?
反过来想想,也因为js是弱类型语言,才会出现这种怪异的现象。
延展学习
在ES5的时候只有函数作用域和全局作用域,es6新增了块级作用域。
块级作用域 {}
块级作用域中的定义函数、var变量会被提升
// 情况一:
if(true){
c = 1
var b = 0
function a(){}
}
console.log(a, window.a); // ƒ a(){} ƒ a(){}
console.log(b, window.b); // 0 0
console.log(c, window.c); // 1 1
// 情况二:
try{
c = 1
var b = 0
function a(){}
}catch {}
console.log(a, window.a); // ƒ a(){} ƒ a(){}
console.log(b, window.b); // 0 0
console.log(c, window.c); // 1 1
函数作用域 function () {}
1.函数作用域中的定义var变量、创建函数
function add() {
c = 1 // 未定义,在函数被调用的时候会在全局创建c
var b = 0 // 不会污染全局
function a(){} // 不会污染全局
}
add()
// console.log(a, window.a); // a is not defined
//console.log(b, window.b); // b is not defined
console.log(c, window.c); // 1 1
其他
可通过手动debugger来查看数据的变化情况,就会发现数据的改变时机。