不知不觉距离上一篇闭包文章已通过了8个月了,如今的理解对比以前要健壮的多,再次总结下花生理解的闭包。javascript
闭包实际上就是子做用域读取父做用域的变量,这原本很合理也很简单,可是关键点在于这个读取是动态的,请看下面的例子:java
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循环的一个很常见的用处是遍历数组。code
var arr =['a' ,'b' ,'c']; for(var i=0 ;i<arr.length ;i++){ setTimeout(function(){ console.log(arr[i]); }); }; // 输出 undefined undefined undefined
由于是动态读取,因此输出undefined很正常。可使用上面的构建一个自执行函数来解决,但还有一个更方便的解决方案,也是实际开发中常常用到的。ip
['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就已经能够解决全部问题了,因此在实际开发中应该避免第一种构建闭包的解决方案。