# 闭包
闭包是函数和声明该函数的词法环境的组合
主要是由 javascript 的两个特性产生的:
- 词法作用域:内部函数可以访问函数外面的变量,是因为函数外面的变量保存在内部函数的词法作用域内,词法作用域的范围是由变量声明的位置决定,例:
function init() {
var name = "Mozilla"; // name 是一个被 init 创建的局部变量
// 在函数内部声明的函数,词法作用域就是外层函数
function displayName() {
alert(name); // 内层函数没有name变量,在外层函数中寻找,使用父函数中声明的变量
}
displayName();
}
init();
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 函数作为值传递(
first class):也就是函数即可以作为参数传入别的函数,也可以作为别的函数的返回值
在一个函数被作为返回值返回的时候,相当于返回了一个通道,这个通道可以访问函数的词法作用域,通过词法作用域也就可以访问到外部函数的变量,即使外部函数已经销毁,这样就产生了闭包,也就是能够读取其他函数内部变量的函数。
# 例子
var a = function() {
var x = 1;
var fn = function() {
console.log(x);
};
fn();
return fn;
};
var b = a();
b();
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
执行函数 a 然后把返回值赋值给了变量 b,函数 a 的返回值是一个函数 fn ,而函数作为返回值被返回的时候,实际上是返回的一个指向 fn 的指针,最后在执行 b 函数,也就是执行 fn 函数,fn 通过词法作用域可以访问 a 中的变量 x ,所以两次执行的结果都是显示 x 的值。
# 特点
- 可以使得函数内部变量只能被访问,无法被修改,访问途径只能通过函数内部提供的接口
- 由于闭包产生的引用,可以使得变量不会被自动回收
大部分应用都是基于上面的特点
# 应用场景
- 匿名自执行函数:函数只执行一次,执行完毕立即释放资源,不会造成全局变量污染,例如 ui 初始化
- 结果缓存:对于某些很耗时的函数,可以通过闭包将计算结果缓存,再次调用函数的时候,如果有缓存就使用缓存,没有在重新计算
- 封装
- 实现类和继承
- nodejs 模块化
NodeJS 会给每个文件包上这样一层函数,引入模块使用 require,导出使用 exports,而那些文件中定义的变量也将留在这个闭包中,不会污染到其他地方
(funciton(exports, require, module, __filename, __dirname) {
/* 自己写的代码 */
})(/* NodeJS 全局变量参数 */);
1
2
3
2
3