# 9. 什么是闭包?闭包的作用是什么?
# 闭包的定义
《JavaScript高级程序设计》:
闭包是指有权访问另一个函数作用域中的变量的函数
《JavaScript权威指南》:
从技术的角度讲,所有的JavaScript函数都是闭包:它们都是对象,它们都关联到作用域链。
《你不知道的JavaScript》
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
# 闭包特性
- (1)函数内再嵌套函数
- (2)内部函数可以引用外层的参数和变量
- (3)参数和变量不会被垃圾回收机制回收
创建一个闭包
function foo() {
var a = 2;
return function fn() {
console.log(a);
}
}
let func = foo();
func(); //输出2
闭包使得函数可以继续访问定义时的词法作用域。拜 fn 所赐,在 foo() 执行后,foo 内部作用域不会被销毁。
# 闭包的作用
能够访问函数定义时所在的词法作用域(阻止其被回收)。
私有化变量
function base() {
let x = 10; //私有变量
return {
getX: function() {
return x;
}
}
}
let obj = base();
console.log(obj.getX()); //10
- 模拟块级作用域
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = (function(j){
return function () {
console.log(j);
}
})(i);
}
a[6](); // 6
- 创建模块
function coolModule() {
let name = 'Yvette';
let age = 20;
function sayName() {
console.log(name);
}
function sayAge() {
console.log(age);
}
return {
sayName,
sayAge
}
}
let info = coolModule();
info.sayName(); //'Yvette'
模块模式具有两个必备的条件(来自《你不知道的JavaScript》)
- 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)
- 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。
鄙视题:
function fun(n,o) {
console.log(o);
return {
fun: function(m) {
return fun(m,n);
}
};
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?
// 问: 三行a,b,c的输出分别是什么?
解答
- 1、第一行a
var a = fun(0); a.fun(1); a.fun(2); a.fun(3); 可以得知,第一个fun(0)是在调用第一层fun函数。第二个fun(1)是在调用前一个fun的返回值的fun函数,所以:
第后面几个fun(1),fun(2),fun(3),函数都是在调用第二层fun函数。
遂:
在第一次调用fun(0)时,o为undefined;
第二次调用fun(1)时m为1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1,n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;
第三次调用fun(2)时m为2,但依然是调用a.fun,所以还是闭包了第一次调用时的n,所以内部调用第一层的fun(2,0);所以o为0
第四次同理;
即:最终答案为undefined,0,0,0
- 2、第二行b
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,? 先从fun(0)开始看,肯定是调用的第一层fun函数;而他的返回值是一个对象,所以第二个fun(1)调用的是第二层fun函数,后面几个也是调用的第二层fun函数。
遂:
在第一次调用第一层fun(0)时,o为undefined;
第二次调用 .fun(1)时m为1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1,n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;
第三次调用 .fun(2)时m为2,此时当前的fun函数不是第一次执行的返回对象,而是第二次执行的返回对象。而在第二次执行第一层fun函数时时(1,0)所以n=1,o=0,返回时闭包了第二次的n,遂在第三次调用第三层fun函数时m=2,n=1,即调用第一层fun函数fun(2,1),所以o为1;
第四次调用 .fun(3)时m为3,闭包了第三次调用的n,同理,最终调用第一层fun函数为fun(3,2);所以o为2;
即最终答案:undefined,0,1,2
- 3、第三行c
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,? 根据前面两个例子,可以得知:
fun(0)为执行第一层fun函数,.fun(1)执行的是fun(0)返回的第二层fun函数,这里语句结束,遂c存放的是fun(1)的返回值,而不是fun(0)的返回值,所以c中闭包的也是fun(1)第二次执行的n的值。c.fun(2)执行的是fun(1)返回的第二层fun函数,c.fun(3)执行的也是fun(1)返回的第二层fun函数。
遂:
在第一次调用第一层fun(0)时,o为undefined;
第二次调用 .fun(1)时m为1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1,n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;
第三次调用 .fun(2)时m为2,此时fun闭包的是第二次调用的n=1,即m=2,n=1,并在内部调用第一层fun函数fun(2,1);所以o为1;
第四次.fun(3)时同理,但依然是调用的第二次的返回值,遂最终调用第一层fun函数fun(3,1),所以o还为1
即最终答案:undefined,0,1,1