乐趣区

关于前端:JavaScript-图解-闭包应用之循环事件绑定的N种解决办法

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 –

退出移动版