了解闭包:闭包最简单的描述就是ECMAScript允许使用内部函数–即函数定义和函数表达式位于另一个函数的函数体内。
要很好的了解闭包,首先了解函数,函数分为三个阶段,函数定义阶段,函数调用阶段,函数的调用。
函数定义阶段:
1.在内存中开辟一个存储空间
2.把函数体内的代码当作字符串一摸一样的放在这个存储空间中, 碰到的所有变量都不进行解析
3.把这个空间地址赋值给函数名(变量名)
函数调用阶段:
1.按照函数名或者变量名找到对应的存储空间
2.重新开辟一个函数执行空间
3.在这个执行空间里面进行形参赋值
4.在这个执行空间里面进行预解析
5.把函数存储空间的代码复制一份到执行空间里面执行一遍
6.执行完毕之后,把这个开辟出来的执行空间销毁
函数调用阶段:
每一次函数调用都会开辟一个执行空间
调用一次,开辟一个执行空间,执行完毕代码销毁。
再次调用的时候,再次开辟一个执行空间,执行完毕代码销毁
了解完函数,接着看闭包,闭包的生成有三个比必要条件:
1.在函数A内部直接或者间接返回一个函数B
2.B函数内部使用着A函数的私有变量(私有数据)
3.A函数外部有一个变量接收着函数B,形成了一个不会销毁的函数空间
function a(){
//这个num变量就是函数a的私有变量
var num = 100;
return function b(){
console.log(num);
}
}
//res接受的就是函数a内部返回的一个复杂数据类型(函数b)
var res = a();
//res接受的就是函数b的执行空间的地址,就是函数a执行以后的返回值
res()
// 当 res 调用的时候, 打印 num
// 打印出来的就是 a 函数内部的私有变量 num 的值
闭包的特点:
1.延长了变量的生命周期
2.可以访问函数内部的私有变量
3.保护私有变量不被外界访问
闭包的缺点:
当一段内存空间中有一个不会被销毁的东西一直存在,就会出现内存占用,如果过多,就会导致内存溢出,结果就是内存泄露。如上例中的res,
var res = a();
// res 接受的就是 a 函数内部返回的复杂数据类型函数 b
// a 函数的函数执行空间就不会销毁
// a 函数执行空间内部的 num 变量也不会销毁
闭包的应用:
循环绑定事件
获取所有 button 按钮
for 循环绑定好一个点击事件
需求: 点击的时候打印出对应的索引
html
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
js
var btns = document.querySelectorAll('button')
function loop(num){
btns[num].onclick = function(){
console.log(num)
}
}
//btns就是外部的一个变量
//btns[0]就是外部的内容
//btns[0].onclick 就是外部的数据
for(var i=0;i<btns.length;i++){
loop(i)
}