不知不觉距离上一篇闭包文章已经过了8个月了,现在的理解对比之前要健壮的多,再次总结下花生理解的闭包。
闭包实际上就是子作用域读取父作用域的变量,这本来很合理也很简单,但是关键点在于这个读取是动态的,请看下面的例子:
for(var i=0 ;i<3 ;i++){
setTimeout(function(){
console.log(i);
});
};
// 输出 3 3 3
结果并不是期望的0 1 2
,因为是动态的读取i,因此如果你在下文改变变量i也依旧会影响到输出读取i。
传统的解决方案是构建闭包,是最有效也是兼容性最好的方法
for(var i=0 ;i<3 ;i++){
(function(num){
setTimeout(function(){
console.log(num);
});
})(i);
};
// 输出 0 1 2
这么做是十分有效的,为每一个闭包单独创建一个作用域,也是下面要说的其他解决方案的基础。
这一段代码很重要,理解这一段代码基本上就可以说理解闭包了。
实际上,大多数情况我们并不是想单纯是使用for循环,for循环的一个很常见的用处是遍历数组。
var arr =['a' ,'b' ,'c'];
for(var i=0 ;i<arr.length ;i++){
setTimeout(function(){
console.log(arr[i]);
});
};
// 输出 undefined undefined undefined
因为是动态读取,所以输出undefined很正常。可以使用上面的构建一个自执行函数来解决,但还有一个更方便的解决方案,也是实际开发中经常用到的。
['a' ,'b' ,'c'].forEach(function(item){
setTimeout(function(){
console.log(item);
});
});
// 输出 a b c
利用Array原生的forEach可以更好的实现,而且也符合语义,这个是花生最推荐的用法。
如果浏览器较新支持ES5,Function还提供一个bind方法来绑定参数
for(var i=0 ;i<3 ;i++){
setTimeout(console.log.bind(null ,i));
};
// 输出 0 1 2
Function.bind具体的语法与兼容性可以参考MDN。
还有其他的“歪门邪道”的解决方案,比如利用闭包读取到父作用域的集合,在集合里寻找“自己”,或者是利用js的引用传递等等。
实际上,利用ES5的bind方法和Array的forEach就已经可以解决所有问题了,所以在实际开发中应该避免第一种构建闭包的解决方案。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。