- 闭包是一种机制,函数执行,产生一个不被释放的上下文,
- 一是保护了上下文里面的私有变量不受外界干扰,
- 二是私有变量的值也保存了下来,可以供其下级上下文调取使用,这种机制叫做闭包。
- 闭包的弊端是会导致栈内存增加,项目中如果滥用闭包,会导致产品性能降低。
JS中获取的每一个DOM元素都是一个元素对象(堆内存),拥有很多的属性和方法;
实现元素点击事件行为:xxx.onclick = function(){...}
,就是给元素对象的私有属性onclick
赋值;
当点击的时候, 浏览器监听到事件触发就会帮我们把赋值的函数执行。
经典案例
给每一个按钮的点击事件赋值打印当前按钮索引值事件。
错误代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>闭包</title>
</head>
<body>
<input type="button" value="1111">
<input type="button" value="2222">
<input type="button" value="3333">
<input type="button" value="4444">
<script>
var btnList = document.querySelectorAll("input[type='button']");
console.log(btnList);
for(var i = 0;i < btnList.length; i++){
btnList[i].myIndex = i;
btnList[i].onclick = function(){
console.log("当前索引是:"+ this.myIndex);
}
};
</script>
</body>
</html>
依次点击按钮的结果显示都是4。
原因图形解析:
解决方法1:自定义属性方案
<!--...-->
<script>
var btnList = document.querySelectorAll("input[type='button']");
for(var i = 0;i < btnList.length; i++){
btnList[i].myIndex = i;
btnList[i].onclick = function(){
console.log("当前索引是:"+ this.myIndex);
}
};
</script>
<!--...-->
依次点击按钮的结果:
图形解析:
原理:
- 在循环事件绑定的时候,给每一个元素对象设置一个自定义属性
myIndex
,存储元素的索引。 - 事件触发的时候,
this
是点击的元素,所以基于元素.myIndex
即可获取到元素的索引。
解决方法2:闭包
<!--...-->
<script>
var btnList = document.querySelectorAll("input[type='button']");
for(var i = 0;i < btnList.length; i++){
(function(x){
btnList[x].onclick = function(){
console.log("当前索引是:"+ x);
}
})(i)
};
</script>
<!--...-->
依次点击按钮的结果:
图形解析:
原理:利用了闭包的保护和保存机制
解决方法3:let
<!--... -->
<script>
var btnList = document.querySelectorAll("input[type='button']");
for(let i = 0;i < btnList.length; i++){
btnList[i].onclick = function(){
console.log("当前索引是:"+ i);
}
};
</script>
<!--... -->
依次点击按钮的结果:
原理:
- 每一轮循环都有基于
let
声明的变量i
,所以循环体会查生一个私有的块级上下文。 i
是私有的,分别存储着对应元素的索引值。- 函数/ 对象以外的大括号(循环/ 判断/ { } )中出现了以
let/ const/ function/ class
声明的方式,就会产生块级上下文。 - var 既不会产生块级上下文,也不会受块级上下文影响。
解决方法4:事件的冒泡传播机制
<!--... -->
<input type="button" class="btn" index="0" value="1111">
<input type="button" class="btn" index="1" value="2222">
<input type="button" class="btn" index="2" value="3333">
<input type="button" class="btn" index="3" value="4444">
<script>
document.onclick=function(ev){
let target = ev.target,
targetName = target.type;
targetClass = target.className;
if(targetName ==="button" && targetClass === "btn"){
console.log("当前索引是:"+ target.getAttribute("index"));
}
}
</script>
<!--... -->
依次点击按钮的结果:
原理:事件委托, 点击文档中的任意元素,都会触发document的点击行为(事件的冒泡机制)