onclick绑定变量_思维导图 | 闭包应用之循环事件绑定的N种解决办法

本文探讨了JavaScript中闭包的概念及其在事件绑定中的应用,包括闭包的优点和弊端。作者通过实例解析了循环事件绑定的问题,并提出了四种解决方案:基于闭包的三种方法(自执行函数、立即调用的函数表达式和let作用域)以及使用自定义属性。文章强调了性能考虑,指出事件委托是最佳实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

▲ 点击上方蓝字关注我 ▲

文 / 景朝霞

来源公号 / 朝霞的光影笔记

ID / zhaoxiajingjing

目录0 / 闭包(1)闭包优点(2)闭包的弊端1 / 闭包应用(1)事件绑定(2)方案一:基于闭包的机制完成第一种闭包第二种闭包第三种闭包(3)方案二:自定义属性(4)方案三:事件委托

0 / 闭包

闭包:函数运行的一种机制(不是某种代码形式)

(1)闭包优点

1、函数执行会形成一个私有上下文,如果上下文中的某些内容(一般是指堆内存地址)被上下文以外的一些事物(例如:变量/事件绑定等)所占用,则当前上下文不能被出栈释放【浏览器的垃圾回收机制GC所决定的】

闭包的机制:形成一个不被释放的上下文

(1)保护:保护私有上下文中的“私有变量”和外界互不影响

(2)保存:上下文不被释放,那么上下文中“私有变量”和“值”都会被保存起来,可以供其上下文中使用

(2)闭包的弊端

弊端:

(1)如果大量使用闭包,会导致栈内存太大,页面渲染变慢,性能受到影响。所以真实项目中需要 合理应用闭包

(2)某些代码会导致栈溢出或者内存泄漏,这些操作都是需要我们注意的

死递归

下面的案例“死递归”:Uncaught RangeError: Maximum call stack size exceeded "内存溢出"

如果用递归,必然需要一个结束的条件

// Uncaught RangeError: Maximum call stack size exceededfunction fn(x){    // console.log(x);    fn(x+1);}fn(1);

△ 死递归

1 / 闭包应用

69cc5b3d6233a4a536a8b40a2862cc81.png

(1)事件绑定
<button>我是1button><button>我是2button><button>我是3button>

△ html

var buttons = document.querySelectorAll('button'); //=> NodeList “类数组”集合for(var i = 0; i < buttons.length; i++){    buttons[i].onclick = function (){        console.log(`当前按钮的索引:${i}`);    };}

△ JS 代码

问:以上的JS代码,能依次点击按钮能输出对应的索引吗?

7feaf5ad08ef538a826b22451410e325.png

△ 图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);}

△ 自执行函数

7eac0e6b9f9a686847263b314a20100a.png

△ 图2_自执行函数图解

基于 闭包 的机制,每一轮循环时都会产生一个闭包,存储对应的索引。点击事件触发,执行对应的函数,让其上级上下文是闭包即可

第二种闭包

基于这个思路,还可以这样写,只要产生闭包就好啦

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}`);    };}

△ 自定义属性

09c1350f85719ad3b4dd225d0de4df52.png

△ 图3_自定义属性

(4)方案三:事件委托
<button index='0'>我是1button><button index='1'>我是2button><button index='2'>我是3button>

△ 添加自定义属性

事件委托:不论点击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}`);    }};

△ 事件委托

在闭包中要占用栈内存,自定义属性中要占用对象堆内存空间写属性,而 事件委托 并没有额外的占用这些空间,性能是最好的

69cc5b3d6233a4a536a8b40a2862cc81.png

- end -

5bf27749760840a9266fcc163d0dffdd.png

从"你"到"更好的你",有无限可能~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值