求你了,别再黑闭包了
求你了,别再黑闭包了
声明:此文无意冒犯任何人,仅为个人学习总结,不适之处欢迎斧正。
引子
\\ Talk is cheap .show me the code.code never lie!NEVER
先看三段被称为js闭包经典面试题的代码
第一种引起for循环问题的原因
// 异步引起的for循环问题 1
for(var i=0;i<5;i++){
setTimeout(function timer(){
console.log(i);
},i*1000);
// 异步引起的for循环问题 2
for (var i = 1; i <= 5; i++) {
item.onclick = function () {
console.log(i);
};
}
上面两段代码经常被拿来解释闭包,百度一下javaScript for循环"首屏中就会出来各种以“彻底弄懂js循环中的闭包问题 ”的博客,然而最后只是华丽丽的推给了闭包,总结起来就是“反正我已经告诉你了,这个问题就是闭包引起的”。
但是这两段代码中结果最终只输出5,真的和闭包没有一毛钱的关系。真正的原因是当给元素绑定事件或者设置定时器函数时就会形成异步;有因为javaScript语言是单线程语言(一次只执行一件事情,在当前事件尚未执行完之前,即使异步函数调用事件到了,也要先把当前的事情执行完);这两段代码开始执行后首先读取到for循环(声明并初始化i的值,对i的值进行判断)然后进入for循环体内,遇到异步函数,把异步函数加入到任务池(所有异步函数都会被到这里,等待立即执行代码执行过后再开始执行),然后一根筋的js继续执行for循环,直到i=5;不再满足for循环的执行条件,便开始执行任务池中的异步事件(即给item元素绑定事件)
第二种引起for循环问题的原因
\\函数作为值引起的for循环问题
for(var i=0,arr=[];i<5;i++){
arr.push(function(){alert(i)});
}
arr[0]()
js中的函数有很多的身份,上面的代码中for循环执行后,每次执行arr.push()函数把匿名函数当作值(此时函数仅仅是一段字符串而已)添加到arr数组中,如此反复执行最终到i=5;for循环停止;在我们通过arr0语句调用它之前,它仅仅是一段字符串,根本不会和i变量产生任何关系(事实上此时i也是函数字符串中的一部分);当我们调用函数,匿名函数首先在函数的作用域寻找变量i,没有找到,于是开始通过作用域链向上级作用域寻找,把全局总用域中的i=5付给i ;
补充
实际上变量名仅仅是一个标识符,js通过它获取对变量值的引用;
// for循环的正常执行状况,可与上面两种情况进行比较理解
for(var i=0;i<5;i++){
console.log(i)
}
//每次for循环都会执行console.log(i)函数,实际上log()函数也形成闭包了
划重点
导致上面两种for循环取值问题的原因,和闭包没有任何关系,真正的原因是for循环改变了自己所在作用域里面的i变量,而函数里面没有i且对for循环作用域里面的变量i的使用是在这个改变之后的;
为什么闭包被用来背黑锅
闭包太常见了,有函数的地方几乎都会有闭包 ,激发条件是函数里面有一个存在于上级作用域的变量;
什么是闭包
闭包,javaScript里面神奇的存在,;在《你不知到的javaScript上卷》中称"理解闭包可以看作某种意义上的重生";但我认为真正难的并不是闭包本身而是人们对闭包的恐惧,是对预解释和js作用域的缺乏了解;是对javaScript语言本身特性理解的缺乏而把一些当前无法理解的问题盲目的踢给闭包,用闭包这个更大的不理解来安慰眼前的不理解。
从定义上来看,所有的函数都可以是闭包。当一个函数在调用时,引用了不是自己作用域内定义的变量(通常称其为自由变量),就形成了闭包;
闭包为什么难?
- 闭包本身涉及知识点较多,
- 各种书籍各成一派,缺乏同一标准;
- 一些博客错把闭包引起的问题说成闭包,对js新人产生很大的误导;
如何掌握闭包
- 牢记上述对闭包的定义,在学习过程中注意区分闭包概念和闭包产生的影响的不同
- 学习并彻底掌握js的作用域知识
- 要掌握js的作用域知识必须先掌握js预解析的知识
附:
js预解析总结
再次声明:自学Js中,还有很多知识点未掌握,本文无意冒犯任何人,不当之处欢迎扶正。