-
基于闭包的机制完成
-
函数执行形成私有上下文,执行完成后,当前上下文有东西被当前上下文以外的东西占用,不能被释放,就是闭包
-
闭包的特点
- 闭包的保护机制:保护当前上下文的变量独立,不受外接干扰
- 闭包的保存机制:保存起来私有变量,形成不释放的上下文呢,供下级上下文使用
-
闭包的弊端:闭包占用内存,消耗浏览器的性能(不能滥用闭包)
1. 循环事件绑定或循环操作中对闭包的应用
1.1 循环事件普通输出
- setTimeout([function], [interval]) 设置一个定时器,等待interval时长,触发function执行
for (var i = 0; i < 3; i++) {
setTimeout(()=>{
console.log(i)
}, (i+1)*1000)
}
- 第一轮循环i=0,设置创建一个定时器,此时并未执行function,1000ms后执行
- 第二轮循环i=1,设置创建一个定时器,此时并未执行function,2000ms后执行
- 第三轮循环i=2,设置创建一个定时器,此时并未执行function,3000ms后执行
- 此时同步代码for循环执行到 i=3 循环结束
- 1000ms到了执行回调,在形成的私有上下文中无i,找上级上下文,也就是全局i = 3
- ... 输出3个3
1.2 循环事件闭包改造
- 思路,在自己上下文中保存一个i,闭包思路
1.2.1 写法1
for (var i = 0; i < 3; i++) {
// 每轮循环,自执行函数执行,都会产生一个私有上下文
// 并且是把当前这一轮循环,全局变量i的值作为实参传递给私有上下文中的形参i
(function(i){
setTimeout(()=>{
console.log(i)
}, (i+1)*1000)
})(i);
}
- 第一轮EC(AN1) 形参赋值 i = 0
- 第一轮EC(AN2) 形参赋值 i = 1
- 第一轮EC(AN3) 形参赋值 i = 2
- 每一个形成的私有上下文中,都会创建一个
箭头函数堆
,并且把值赋值给了window.setTimeout,这样等价于当前上下文中的某些内容被上下文以外的内容占用了,形成的上下文不会被释放,私有变量i的值也不会被释放,闭包
,循环三次形成三个闭包
1.2.2 写法2
// let xxx = proxy(0); // -> proxy执行会返回一个函数,被xxx占用,不释放,闭包,闭包中私有变量,就是函数执行传递的参数
const proxy = i => {
return () => {
console.log(i);
};
}
for (var i = 0; i < 3; i++) {
// 回调执行的是proxy返回的小函数
setTimeout(proxy(i), (i+1)*1000)
}
1.2.3 写法3
- let会形成私有的块级作用域
- 基于let的循环,let存在块级作用域的处理机制,首先浏览器会创建一个父级私有上下文,来控制循环
- 每一轮循环还会产生一个私有的块级上下文,都有自己的私有变量i,存储当前这一轮循环i的值
- 每一个私有块级上下文中,也是创建一个函数,并且被window.setTimeout给占用了,不会释放这个块级上下文,
闭包
for (let i = 0; i < 3; i++) {
setTimeout(()=>{
console.log(i)
}, (i+1)*1000)
}
- let形成的闭包,是浏览器底层实现的,从性能上比自己创造的闭包要快一点
for (let i = 0; i < 3; i++) {
// 每一轮循环都会产生一个私有的块级上下文,如果上下文中没有什么东西被外部占用,则本轮循环结束,块级上下文被释放
// 一旦有东西被占用,则会产生闭包
setTimeout(()=>{
// 创建函数作用域就是上下文,与x无关,也形成闭包
// 与i无关的代码,setTimeout被占用,也是闭包
}, 1000)
}
- 不需要i推荐写法,这种情况,不会产生块级上下文,不形成闭包
let i = 0;
for(; i<3; i++) {
setTimeout(()=>{
...
}, 1000)
}