0 / 闭包
闭包:函数运行的一种机制(不是某种代码形式)
(1)闭包优点
1、函数执行会形成一个私有上下文,如果上下文中的某些内容(一般是指堆内存地址)被上下文以外的一些事物(例如:变量/事件绑定等)所占用,则当前上下文不能被出栈释放【浏览器的垃圾回收机制GC所决定的】
<u>闭包</u>的机制:形成一个不被释放的上下文
(1)保护:保护私有上下文中的“私有变量”和外界互不影响
(2)保存:上下文不被释放,那么上下文中“私有变量”和“值”都会被保存起来,可以供其上下文中使用
(2)闭包的弊端
弊端:
(1)如果大量使用闭包,会导致栈内存太大,页面渲染变慢,性能受到影响。所以真实项目中需要 <u>合理应用闭包</u>
(2)某些代码会导致栈溢出或者内存泄漏,这些操作都是需要我们注意的
死递归
下面的案例“死递归”:Uncaught RangeError: Maximum call stack size exceeded
"内存溢出"
如果用递归,必然需要一个结束的条件
// Uncaught RangeError: Maximum call stack size exceeded
function fn(x){
// console.log(x);
fn(x+1);
}
fn(1);
△ 死递归
1 / 闭包应用
(1)事件绑定
<button>我是1</button>
<button>我是2</button>
<button>我是3</button>
△ html
var buttons = document.querySelectorAll('button'); //=> NodeList “类数组”集合
for(var i = 0; i < buttons.length; i++){
buttons[i].onclick = function (){
console.log(`当前按钮的索引:${i}`);
};
}
△ JS 代码
问:以上的JS代码,能依次点击按钮能输出对应的索引吗?
△ 图1_事件执行
每次点击触发函数执行时,获取的i
都是全局的,也就是循环结束后的结果3
那么,如何解决这个问题呢?
(2)方案一:基于闭包的机制完成
第一种闭包
var buttons = document.querySelectorAll('button');
for(var i = 0; i < buttons.length; i++){
(function (i){
/*【自执行匿名函数】
每一轮循环都会形成一个闭包
存储私有变量i的值,当前循环传递进来的i值
(1)自执行函数执行,产生一个私有的执行上下文EC(A),私有形参变量i=0/1/2
(2)EC(A) 上下文中创建一个小函数,并让全局的buttons中的某一项占用创建的小函数
*/
buttons[i].onclick = function (){
console.log(`当前按钮的索引:${i}`);
};
})(i);
}
△ 自执行函数
△ 图2_自执行函数图解
基于 <u>闭包</u> 的机制,每一轮循环时都会产生一个闭包,<u>存储对应的索引</u>。点击事件触发,执行对应的函数,让其上级上下文是闭包即可
第二种闭包
基于这个思路,还可以这样写,只要产生闭包就好啦
var buttons = document.querySelectorAll('button');
for(var i = 0; i < buttons.length; i++){
buttons[i].onclick = (function (i){
return function (){
console.log(`当前按钮的索引:${i}`);
};
})(i);
}
△ 闭包:自执行函数
let obj = {
fn:(function() {
// 自执行函数:把return的小函数赋值给obj.fn了
console.log('大函数');
return function () {
console.log('小函数');
};
})()
};
obj.fn(); //=> 每次调用时,执行的是【小函数】这个函数
第三种闭包
var buttons = document.querySelectorAll('button');
for (let i = 0; i < buttons.length; i++) {
buttons[i].onclick = function () {
console.log(`当前按钮的索引:${i}`);
};
}
△ 通过let形成闭包
(3)方案二:自定义属性
自定义属性的性能要比闭包好。
循环多少次闭包会形成多少个执行上下文,那如果有100个按钮,1000个按钮呢?非常耗性能
var buttons = document.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++) {
// 每一轮循环都给当前按钮(对象)设置一个自定义属性:存储它的索引
buttons[i].myIndex = i;
buttons[i].onclick = function () {
// this:当前点击的按钮
console.log(`当前按钮的索引:${this.myIndex}`);
};
}
△ 自定义属性
△ 图3_自定义属性
(4)方案三:事件委托
<button index='0'>我是1</button>
<button index='1'>我是2</button>
<button index='2'>我是3</button>
△ 添加自定义属性
事件委托:不论点击BODY中的谁,都会触发BODY的点击事件
ev.target
是事件源:具体点击的是谁
document.body.onclick = function (ev){
var target = ev.target,
targetTag = target.tagName;
// 点击的是【BUTTON 按钮】
if(targetTag === 'BUTTON'){
var index = target.getAttribute('index');
console.log(`当前按钮的索引:${index}`);
}
};
△ 事件委托
在闭包中要占用栈内存,自定义属性中要占用对象堆内存空间写属性,而 <u>事件委托</u> 并没有额外的占用这些空间,性能是最好的
- end -
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。