闭包的概念
函数执行时形成一个私有作用域(栈内存), 保护私有变量不受外部的影响, 这种保护机制称为闭包
大部分的开发者认为的闭包: 函数执行时形成一个不销毁的私有作用域, 这就是闭包
关于闭包的技术名词
function fn() {
return function() {
...
}
}
let fn2 = fn()
因为fn内部return的函数被外部使用, 所以堆内存不会被释放
这种闭包有个高大上的名字叫柯理化函数
let utils = !function() {
return {
...
}
}()
和上面类似, 这种闭包也有个高大上的名字叫惰性函数
但是, 在真实项目中, 为了保证js的性能(堆栈内存的性能优化), 应该尽可能的减少闭包的使用
闭包的作用
- 闭包可以保护私有变量不受外界的干扰
- 闭包可以形成不销毁的栈内存, 把一些值保存下来, 来供后面使用
闭包应用实战(循环绑定事件, 索引触发)
假如我们页面上有三个按钮, 编号为1,2,3, html结构如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div class="button-wrapper">
<button id="btn">1</button>
<button id="btn">2</button>
<button id="btn">3</button>
</div>
</body>
</html>
我们希望点击button, 把它们对应的索引打出来(从0开始)
我们可能会写出这样的js代码
let buttons = document.querySelectorAll('#btn')
for(var i = 0;i < buttons.length;i++) {
buttons[i].onclick = function() {
console.log(i)
}
}
但是, 当我们点击每个按钮时, 发现无论点击哪个按钮, 都会打印3
造成这样的结果是因为, 当我们点击元素时, 会执行console.log(i)
, 函数作用域内没有i变量, 所以会按照作用域链向上查找, 直到找到了全局变量i, 但是i已经经历过了for循环, 所以值为3
解决方案1: 利用自定义属性记录当前索引
let buttons = document.querySelectorAll('#btn')
for(var i = 0;i < buttons.length;i++) {
buttons[i].Index = i
buttons[i].onclick = function() {
console.log(this.Index)
}
}
自定义属性非常有用里
解决方案2: 把var改为let
let buttons = document.querySelectorAll('#btn')
for(let i = 0;i < buttons.length;i++) {
buttons[i].onclick = function() {
console.log(i)
}
}
console.log(i)
let具有块级作用域, 推荐使用
解决方案3: 利用闭包的保存机制(栈内存可以保存值)
let buttons = document.querySelectorAll('#btn')
for(var i = 0;i < buttons.length;i++) {
buttons[i].onclick = (function(i) {
//这里执行时形成一个私有作用域, 内存不会被释放, 因为返回的函数被外部占用了.
//这个私有作用域把每个i保存了下来
return function() {
console.log(i)
}
})(i)
}
这种方案特别耗费性能, 所以不推荐
网友评论