导言
做了这么多年前端,我觉得关于块效果域这个奇特的魔法,我仍是知之甚少;翻开 MDN,总结一下块效果域的特色:
1-3都是众所周知的,举 MDN 上的几个例子一笔带过:
// demo1
var x = 1;
{
var x = 2;
}
console.log(x); // 输出 2
// demo2
let x = 1;
{
let x = 2;
}
console.log(x); // 输出 1
// demo3
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[0](); // 10
a[1](); // 10
a[6](); // 10
/********************/
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[0](); // 0
a[1](); // 1
a[6](); // 6
块效果域内的函数声明
重点来看一看第四点,先上 MDN 的案例,先不看答案,请我们自己做一做依次打印出内容:
console.log("outside top",foo);
{
foo('inside');
function foo(location) {
console.log('foo is called ' + location);
}
foo('inside');
foo = 2;
console.log("inside",foo);
}
console.log("outside bottom",foo)
查看答案:
console.log("outside top",foo); // outside undefined
{
foo('inside'); // 正常工作而且打印 'foo is called inside'
function foo(location) {
console.log('foo is called ' + location);
}
foo('inside'); // 正常工作而且打印 'foo is called inside'
foo = 2;
console.log("inside",foo); // inside 2
}
console.log("outside bottom",foo) // outside bottom function foo(){}
看了答案之后其实我仍是一头雾水,我仍是不知道这一段代码是如何履行的,是时候亮出我的“杀手锏”了:debug,能够通过 debug 来看一看履行时的效果域链,有助于分析问题的本质;在榜首行代码添加一个 debug 然后在浏览器中履行:
大局效果中居然声明晰一个 foo 变量!此刻效果域为 Global: { foo:undefined }
持续往下调试:
块效果域顶层能够拜访到 foo 函数声明!而且此刻大局效果域上的 foo 为 undefined!此刻全体的效果域为
Block:{
foo:function foo(){}
}
Global:{
foo:undefined
}
持续往下,foo 函数声明之后,在 foo('inside')
代码之前,大局效果域上的 foo 值变为函数了!此刻效果域变为:
Block:{
foo:function foo(){}
}
Global:{
foo:function foo(){}
}
然后块内 foo 被赋值为 2,Block 效果域上的 foo 值为 2,此刻效果域变为:
Block:{
foo:2
}
Global:{
foo:function foo(){}
}
此刻搜索 foo 的值会先找最近的块效果域,找到 foo 对应的值为 2,那么就回来 2。
块效果域完毕后打印 foo 变量,此刻拜访的是大局效果域上的 foo,自然便是 foo 函数声明晰。现在从效果域的视点搞清楚了全体的履行逻辑,但是函数到底是怎样提升的呢?
块效果域内的函数提升
我直接说我的猜测(或许不一定正确,如果有不对之处敬请我们指出来):
var foo;
console.log("outside top",foo);
{
let foo = function foo(location) {
console.log('foo is called ' + location);
}
foo('inside');
window.foo = foo;
foo('inside');
foo = 2;
console.log("inside",foo);
}
console.log("outside bottom",foo)
- 首先将 foo 变量提升到大局效果域,但是不赋值;
- 其次将 foo 函数声明提升到块效果域顶层
- 在原函数声明位置,对 window 上的 foo 变量赋值为 foo 函数
注意
讲了这么多,最终要落实到代码中来,那便是尽量不要在块中声明函数,这么多怪异的操作会导致程序产生无法猜测的 bug