闭包

定义:函数 和 函数内部能访问到的变量 就是一个闭包

常用方式示例:

1
2
3
4
5
6
7
8
9
10
11
  function foo() {
var num = 10;
return function () {
var age= 20;
console.log(++num);
console.log(++age);
}
}
var f = foo();
f(); //11 21
f(); //12 21

通过闭包就可以让外部作用域访问函数内部作用域的变量,我们知道当函数执行完以后,会立即销毁,但 foo 函数内的 num 属性被内部的匿名函数引用着,而内部函数又被外部变量 f 引用着,所以函数 foo 在执行完以后虽然会立即销毁,但它内部的匿名函数在创建的时候就会随之创建一个特殊的容器,用于保存 上层作用域 中 变量 的引用,所以foo 函数中的 num 并不会销毁,当执行第一次 f() 时,会创建 f() 对应的作用域,其中num 会从之前创建的特殊的容器中取出上层作用域中变量的值,而 age 会立即声明一个,当函数执行完毕后,就会销毁对应的作用域,此时 age 也会随之销毁,但变量 f 对应的指针地址不会变,当再次执行 f() 时,又会再创建一个 f() 对应的作用域,num 还是会从上层作用域中拿,但 age 还是会重新声明,使用完后还是会被销毁。
很显然这样会一直持有对 num 的引用,无法进行回收,造成内存占用,所以当不使用时,可以把 f 置为null,来让垃圾回收器进行回收。

闭包优点和缺点

优点

减少全局变量的使用,保证了内部变量的安全,同时外部函数也可以访问内部函数的变量
在内存中维持一个变量,也可以用作缓存

缺点

被引用的内部变量不能被销毁,增大了内存消耗,使用不当易造成内存泄露,解决办法可以在内部变量不使用时,把外部的引用置为 null
闭包就是函数间的跨作用域访问,会导致性能损失

经典面试题


1.请问一下代码会输出?
1
2
3
4
5
6
7
for(var i = 0; i < 5; i++){
setTimeout(function () {
console.log(i);
}, 1000);
}

答:会输出6个5,首先先输出最下边的那个5,因为for完成后 i 的值为5,然后过1秒钟再同时输出5个5,因为for设置了5的定时器,几乎会同时输出
2.问如果想输出 5 0 1 2 3 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
for (var i = 0; i < 5; i++) {
output(i)
}
console.log(i);

function output(i) {
setTimeout(function () {
console.log(i);
}, 1000);
}

或者使用立即执行函数:
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i);
}, 1000);
})(i);
}

console.log(i);

答:第一个5很好输出,因为for循环以后i就会变成5,剩下的我们可以使用立即执行函数来做,首先 JS中调用函数传递参数都是值传递 ,所以当立即执行函数执行时,首先会把参数i的值复制一份,然后再创建函数作用域来执行函数,循环5次就会创建5个作用域,所以1秒后几乎会同时输出0 1 2 3 4