JavaScript Function Closures

官方对闭包的解释: 一个拥有许多变量和绑定这些变量的环境的表达式(通常是一个函数)

闭包的特点

  • 作为一个函数变量的一个引用, 当函数返回时, 其处于激活状态
  • 一个闭包就是当一个函数返回时, 一个没有释放资源的栈区

简单理解,JavaScript允许使用内部函数, 即函数定义和函数表达式位于另一个函数的函数体内. 而且, 这些内部函数可以访问它们所在的外部函数中声明的所有局部变量和参数以及声明的其它内部函数. 当其中一个这样的内部函数在包含它们的外部函数被调用时, 就会形成闭包.

1
2
3
4
5
6
7
8
9
function closure() {
var bar = 'the power of imagination makes us infinite';
return function () {
console.log(bar);
}
}

var foo = closure();
foo();

上述代码, 变量bar定义在函数closure中, 属于局部变量. 若变量bar在closure函数调用完成以后不能再被访问, 在函数执行完成后变量bar将被释放. 但是由于函数closure返回一个内部函数, 而且返回函数引用了变量bar, 导致了变量bar可能会在closure函数执行完成以后还会被引用, 所以变量bar占用的资源不会回收, 这时函数closure就形成一个闭包.

JavaScript垃圾回收(GC)的原则是: 如果一个对象不再被引用,那么这个对象会被垃圾回收器回收. 如果两个对象无干扰的互相引用, 那么则两个对象也会被回收.

为执行的函数提供参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// setTimeout()方法可以延迟执行某个函数
// 第一个参数是需要执行的函数或者代码
// 第二个参数是延迟的毫秒数
function say() {
console.log('life is not lack of beauty, just missing a pair of eyes to find beauty');
}
setTimeout(say(), 1000);

// 在setTimeout方法中我们无法给需要的延迟执行的函数传递参数
// 使用闭包我们这样做
function say(words) {
return function () {
console.log(words);
}
}
setTimeout(say('there is no end to learning'), 1000);

模拟静态私有变量

JavaScript本身不支持面向对象(OOP)的特性. 但是我们可以通过JavaScript的一些特性模拟实现JavaScript面向对象.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 定义一个类, 模拟静态私有变量
var ClassPerson = (function() {
var a = 0; // 静态私有变量
return function () {
this.getA = function () {
return a;
}
// constructor
a += 1;
}
})();

var bar = new ClassPerson();
console.log(bar.getA()); // 1

var foo = new ClassPerson();
console.log(bar.getA()); // 2
console.log(foo.getA()); // 2

var bingo = new ClassPerson();
console.log(bar.getA()); // 3
console.log(bingo.getA()); // 3

在上面的例子中, 使用变量a记录ClassPerson被实例化的次数, 使用闭包将变量a模拟为静态私有变量, 每次ClassPerson被实例化时将变量a的值加一. 定义在外层函数内或者内层函数外的成员类似静态成员. 这种形式的代码称作静态封装环境.

参考链接