Skip to content

什么是闭包?

在 JavaScript 的函数(Function)中,使用变量时候的查找顺序是:当前方法 -> 当前方法的执行环境 -> window作用域

查找的顺序被称为作用域链(prototype),我们常说的 JS 天生是继承的,就是因为 JS 有作用域链,所有的属性都会在作用域链中供你使用。

在函数中使用变量,这个变量如果在函数执行完毕后没有在其他地方被使用,则该变量会被删除解除引用。但是如果你在函数中返回了一个匿名函数呢,情况就会很有意思了:

javascript
var closure = (function () {
	var i = 0;
	return function () {
		console.log(i++);
	};
})();

closure();

执行结果如下:

javascript
closure() // o
closure() // 1
closure() // 2
// ...

这样的情况是因为返回的函数和使用到父作用域的变量形成了一个闭包,父级执行环境的作用域链会被销毁,但活动对象保留了,变量会一直存在于内存中,不会被清除,且计算结果也会一直被保存

那么,如果我们一直使用闭包,当然没有问题,但是当我们不使用时,我们因当如何去清除闭包呢?毕竟我们的内存是有限的。为了我们的性能优化,去清除闭包就尤为重要了。

如何清除闭包

简单粗暴的直接 closure = null 就会销毁闭包和它所使用到的活动对象。

模拟 Java 中类的定义

javascript
(function () {
	var name = '';

	Personal = function (value) {
		name = value;
	};

	Personal.prototype.getName = function () {
		return name;
	};

	Personal.prototype.setName = function (value) {
		name = value;
	};
})();

//由于内部Personal定义事没有使用var,所以默认定义为全局变量
var p1 = new Personal('Michael');
p1.getName(); //Michael
p1.setName('Greg');
p1.getName(); //Greg

var p2 = new Personal('MM');
p2.getName(); //MM
p1.getName(); //MM

//由于都是使用Personal创建的实例,所以name就成了所有实例的共享属性。