闭包的定义和特性
闭包
是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量。
闭包有下面三个特性:
- 函数嵌套函数。
- 函数内部可以引用外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
下面看个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function f1() { let i = 0; const f2 = function() { ++i; console.log(i); } return f2; } let fun = f1(); fun(); fun(); fun = null;
|
在上面这段代码中,fun
一共运行了两次,第一次1
,第二次2
。这证明了函数f1
中的局部变量i
一直保存在内存中,并没有在fun
调用后被自动清除。
为什么会这样呢?原因就在于f1
是f2
的父函数,而f2
被赋给了一个全局变量,这导致f2
始终在内存中,而f2
的存在依赖于f1
,因此f1
也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection
)回收。
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题。在IE
中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
接下来看一个经典的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 1000 * i); } for (let i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 1000); } for (var i = 0; i < 5; i++) { (function(i) { setTimeout(function() { console.log(i); }, 1000 * i); })(i); } for (var i = 0; i < 5; i++) { setTimeout((function(i) { return function() { console.log(i); } })(i), i * 1000); }
|
下面有几句话帮助理解闭包:
闭包是一个有状态(不消失的私有数据)的函数
打破作用域规则的变量就是闭包