一、引例
需求:三个按钮,每个按钮点击时,弹出其对应序号
<button>第一个按钮</button>
<button>第二个按钮</button>
<button>第三个按钮</button>
错误的写法
var bts = document.querySelectorAll('button'); for(var i = 0; i < bts.length; i++) { let bt = bts[i]; bt.onclick = function() { console.log('这是第 ' + (i + 1) + '个按钮'); } } // 无论点哪个按钮,都会输出为第四个按钮
由于
var i
为全局变量,因此当onclick
所绑定的函数执行时,i 早已变为了3ES6的写法
for(let i = 0, length = bts.length; i < length; i++) { // 其余代码不变 }
let i
为局部变量,每个onclick
绑定函数的 i 都是独立的,互不影响;
另外,bts.length
每次执行for
语句时都会重新计算,因为bts
是伪数组,因此,为了减少计算量,将其放在let i = 0, length = bts.length
闭包的写法
for(let i = 0, length = bts.length; i < length; i++) { (function (j) { let bt = bts[j]; bt.onclick = function() { console.log(i + 1); } })(i); }
二、闭包的概念
**内部函数**
使用了**外部函数**
的值,就产生了闭包(外部函数需要被调用)- 闭包就是
嵌套的内部函数
,即下面的w_in()
```javascript function w_out() { let a = 1; let b = 2; function w_in() { // w_in 就是闭包 console.log(a); } w_in(); }
w_out();
闭包中,由于只引用了外部的 **a**,因此 **b** 并不包含在闭包中,如下图<br />
闭包在代码**编译时**就产生了,并不需要调用
```javascript
function w_out() {
// 第 2 行时,闭包就已经产生(由于函数提升)
let a = 1;
function w_in() {
a++;
}
return w_in;
}
当闭包成为垃圾对象时,闭包死亡
function w_out() {
let a = 1;
function w_in() {
a++;
}
return w_in;
}
let w = w_out();
w = null; // 此时,闭包死亡
三、常用的闭包(2种)
1、将函数作为另一个函数的返回值
由于函数执行上下文会在函数调用完毕后,自动删除,为了防止其某些内部变量消失,可采用闭包的做法
(非闭包内的局部变量,执行完毕后会被删除)
function w_out() {
let a = 1;
function w_in() {
a++;
console.log(a);
}
return w_in;
}
let w = w_out();
w(); // 2
w(); // 3
2、将函数作为实参给另一个函数(setInterval)
function showDelay(msg) {
setInterval(function() {
console.log(msg);
}, 2000);
}
showDelay("this is message !!!");
注意:第一个参数 function() {...}
才是闭包,setIntetval
自身不是闭包
四、闭包的作用
1、延长局部变量的生命周期
非闭包内的变量都会被删除,包括闭包本身的函数名!
function w_out() {
let a = 1;
let b = 1;
function w_in() {
a++;
console.log(a);
}
return w_in;
}
let w = w_out();
w(); // 2
w(); // 3
上述代码中,b
、w_in 这个**变量名**
都会被释放,但w_in
所对应的函数对象不会被释放,而是指向了 w
2、使得外部能够访问内部变量
五、模块化(基于闭包)
1、普通方法
- moudle.js
```javascript
function test() {
let way1 = () => {
} let way2 = () => {console.log('way1()');
} return {console.log('way2()');
} }way1: way1, way2: way2
// vue 中可使用 export function test() {…} 替代
- **index.html**
```javascript
<script src="./moudle.js"></script>
<script>
let w = test();
w.way1();
w.way2();
</script>
2、匿名函数自调用方法
- moudle.js
```javascript
(function() {
let way1 = () => {
} let way2 = () => {console.log('way1()');
} window.w = { // 在 window 中绑定,等于 var w = {…}console.log('way2()');
} })()way1: way1, way2: way2
- **index.html**
```javascript
<script src="./moudle.js"></script>
<script>
w.way1();
w.way2();
</script>
显然,匿名函数自调用更方便,不用额外执行一次原函数
六、闭包的缺点
容易导致内存溢出,因此需要确保完成闭包后,及时将闭包删除