最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • JavaScript | 图解 | 闭包应用之循环事件绑定的N种解决办法

    正文概述 掘金(鲸鲸景鲸叻)   2020-12-15   479

    0 / 闭包

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

    (1)闭包优点

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

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

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

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

    (2)闭包的弊端

    弊端:

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

    (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代码,能依次点击按钮能输出对应的索引吗?

    JavaScript | 图解 | 闭包应用之循环事件绑定的N种解决办法

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

    △ 自执行函数

    JavaScript | 图解 | 闭包应用之循环事件绑定的N种解决办法

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

    △ 自定义属性 JavaScript | 图解 | 闭包应用之循环事件绑定的N种解决办法

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

    △ 事件委托

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

    - end -


    起源地下载网 » JavaScript | 图解 | 闭包应用之循环事件绑定的N种解决办法

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元