前言
今天刷红宝书看到了闭包,凭着再掘金摸爬滚打几个月的经历,一路踏过正则,原型,对象继承,终于还是在这里卡壳了。
注:
本文参考自Javascript高级编程及思否提问,死否文章。
例子
关于闭包有个很典型的例子:
function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function() {
return i;
};
}
return result;
}
let a = createFunctions();
console.log(a[0]());
这要按照我们所想,肯定打印的是0,然而答案是不管打印第几个,这个函数数组的结果都是10。
解释
上面贴的第一个链接,回答中有这么精彩的一段:
函数1作用域
for(var i = 0; i < 10; i++) { 函数1作用域
我在函数1作用域中
result[i] = function() { 函数2作用域
我在函数2作用域中
return i;
};
}
函数1作用域
console.log(i); 毫无疑问,执行到这里的时候,i是10,既然这里是10
那么在函数2作用域中访问i也是10也就不足为奇了
因为函数2作用域中没有,向上去函数1作用域中找
因此,可以看出,这个闭包主要有两个作用域,作用域2因为没有i的存在,自然只能去1中寻找,自然只能是10了,因为闭包运行时i在作用域1中即为10.
引申
这里主要提到了两种帮助我们加深理解的方式:
块1作用域
for(let i = 0; i < 10; i++) { 块2作用域
我在块2作用域中
console.log(i); // 毫无疑问,这里的i从0依次增加到10
result[i] = function() { 块3作用域
我在块3作用域中
return i;
};
}
块1作用域
为什么把var换成let,得到的函数数组的索引就不都是10了呢?我们都知道let可以创建块级作用域,因此每次for循环,都是一个新的块级作用域,而这个作用域与全局的作用域并不是同一个,自然就可以打印出1-10了。
同时我们可以创建另一个匿名函数强制让闭包的行为符合预期:
function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = (function(num) {
return function() {
return num;
};
})(i);
}
return result;
}
let a = createFunctions();
console.log(a[0]());
这里我们没有把闭包赋值给数组,而是定义了一个匿名函数,讲立即执行该匿名函数的结果赋给数组,同时这个匿名函数有个参数num,即最后要返回的值,在调用每个匿名函数时,我们传入了i,使得每个函数都有自己的i的copy。
当然还有别的方式:
function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = (function(num) {
return i;
})();
}
return result;
}
let a = createFunctions();
console.log(a);
多省心。
手动狗头~