这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战
闭包
什么是闭包?
一个函数和对其周围状态的引用绑定再一起,这样的组合就是闭包。通常我们会讲:闭包是一个可以访问外部作用域的内部函数,即使这个外部作用域已经执行结束。
一般情况下,都是由一个内部函数的执行,会在执行的过程中访问到它的外部函数。每创建一个函数,闭包就会在函数创建的同时被创建出来。
function init() {
let a = 1
return function() {
console.log(a)
}
}
init()
闭包的使用中,我们可以利用它们的特性坐一些数据的隐藏和封装。
实例1:
例如下边这个例子
我们想得到的结果是,每点击一个,就改变?为当前的位数。例如点击第一个为1,点击第二个为2等等
js代码如下
function changeText(id,text) {
document.getElementById(id).innerHTML = text;
}
function setupChange() {
var textList = [
{'id': '1', 'text': '当前点击的是第1位'},
{'id': '2', 'text': '当前点击的是第2位'},
{'id': '3', 'text': '当前点击的是第3位'},
{'id': '4', 'text': '当前点击的是第4位'},
];
for (var i = 0; i < textList.length; i++) {
var item = textList[i];
document.getElementById(item.id).onclick = function() {
changeText(item.id, item.text);
}
}
}
setupChange();
这里我们发现一个问题,当前的代码,无论是点击哪个,都只有最后一个改变并且为4。这里是因为作用域的关系,我们最后函数中的changeText中的id和text都为最后一个,因为用的var。
那么修改方式有以下几种
修改方法1
使用let,使用var产生的是全局作用域,let是块级作用域,
修改如下:
let item = textList[i];
修改方法2
我们在修改下事件的调用,让回调不再共享同一个环境
修改如下:
function makeChangeBack(id, text) {
return function() {
changeText(id, text);
};
}
document.getElementById(item.id).onclick = makeChangeBack(item.id, item.text);
makeChangeBack就为我们的事件产生了一个个不同的语法环境
修改方法3
使用匿名的闭包
修改如下:
(function() {
var item = textList[i];
document.getElementById(item.id).onclick = function() {
changeText(item.id, item.text);
}
})()
在这里循环了一个自执行的匿名函数
解决方法4
使用forEach
textList.forEach(function(item) {
document.getElementById(item.id).onclick = function() {
changeText(item.id, item.text);
}
});
性能
一般而言,闭包在处理速度和内存消耗上是比较大的,在非特定环境,一般不是用闭包