小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
通过一个需求来了解
需求:点击某个按钮, 提示点击的是第n个按钮
错误做法
<button>测试1</button>
<button>测试2</button>
<button>测试3</button>
<script type="text/javascript">
var btns = document.getElementsByTagName('button')
for (var i = 0,length=btns.length; i < length; i++) {
var btn = btns[i]
btn.onclick = function () { //遍历加监听
alert('第'+(i+1)+'个') //结果 全是[4]
}
}
</script>
错误原因:直接修改并使用全局变量[i],导致for循环结束后,所有点击按钮绑定的弹窗值都是[i+1],随后调用时,都会找到[i]这个全局变量,但是此时i==3,所以所有结果都是4
可能有朋友已经想到用let或者给btn对象加个属性保存i值了,但是为了学习闭包,这里将会用闭包的思维来解决,在展示代码之前,我们先认识什么是闭包。
闭包
闭包是嵌套的内部函数,当一个嵌套的内部(子)函数应用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。
产生闭包的条件
-
函数嵌套
-
内部函数引用了外部函数的数据(变量/函数) 作用
-
使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
-
让函数外部可以操作(读写)到函数内部的数据(变量/函数)
闭包做法
//利用闭包
for (var i = 0,length=btns.length; i < length; i++) {
//此处的j是局部的,它将传入的[i]存入局部的[j]中,这样就能实现效果
(function (j) {
var btn = btns[j]
btn.onclick = function () {
alert('第'+(j+1)+'个')
}
})(i)
}
因为
(function (j) {})(i)内部嵌套了一个function () {alert('第'+(j+1)+'个')},且内部函数运用了外部函数的变量(i),所以闭包就形成了,因为闭包的特点:使用函数内部的变量在函数执行完后, 仍然存活在内存中,所以,循环执行(function (j) {})(i)之后,该函数的形参j(即每次循环传进来的i)依然存在内存中,所以每次调用都是想要的值(i)。
闭包的缺点
- 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
- 容易造成内存泄露 解决:
- 能不用闭包就不用
- 及时释放
一条简单面试题
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); //?
解析:这个比较简单,that被function(){return that.name;}使用,所以保存的是object里的this值,所以输出的是object的name(My Object)。