什么是闭包

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式就是在一个函数内部创建另一个函数。闭包可以用于设计私有的方法和变量,避免全局变量污染,一般应该避免在全局作用域下定义过多的变量及函数。

特点

闭包的缺点是会常驻内存,会增大内存使用量,使用不当容易造成内存泄漏,所以只有在绝对必要的情况下才使用闭包。

  1. function fn() {
  2. var a = 0;
  3. a++;
  4. return a;
  5. }
  6. console.log(fn()); // 输出: 1
  7. console.log(fn()); // 输出: 1

以上代码每次调用完fn()后,函数内的a变量就会被从内存中销毁,故每次输出的结果都是1,a变量定义在函数内部,函数外部无法访问。如此声明的变量有两个特点: 私有、无法长期存在。

  1. var a = 0;
  2. function fn() {
  3. a++;
  4. return a;
  5. }
  6. console.log(fn()); // 输出: 1
  7. console.log(fn()); // 输出: 2

以上代码在全局环境下声明了a变量,a变量此时作为全局变量会长期存在,调用fn()函数时只是对全局变量a进行赋值操作。如此声明的变量有两个特点: 公开、可以长期存在。

如果我们想要包装一个私有且可以长期存在的变量,那么可以使用闭包:

  1. var fn = (function() {
  2. var a = 0;
  3. return function() {
  4. a++;
  5. return a;
  6. }
  7. })();
  8. console.log(fn()); // 输出: 1
  9. console.log(fn()); // 输出: 2

我们在一个外部匿名函数内声明a变量,然后返回一个内部匿名函数,此时,外部匿名函数定义的变量或方法就长期驻存于内存中,且只有这个外部匿名函数及内部匿名函数可以使用这些变量或方法。我们把外部匿名函数即刻调用,将返回结果(内部匿名函数)保存为fn,每次调用fn就会对a变量累加。用闭包包装的变量具有两个特点: 私有、可以长期存在。

应用

闭包只能取得包装函数中任何变量的最后一个值。

  1. function createFn() {
  2. var arr1 = [];
  3. for(var i = 0; i < 5; i++) {
  4. arr1[i] = function() {
  5. console.log(i);
  6. };
  7. }
  8. return arr1;
  9. }
  10. var arr2 = createFn();
  11. for(var i = 0; i < arr2.length; i++) {
  12. arr2[i](); // 输出的都是: 5
  13. }

以上代码中,createFn()函数内包装了一个长度为5的数组,每个数组元素保存的值是一个函数,整个createFn()是一个闭包,每个数组元素包装的函数引用的都是闭包内的i,所以循环结束后i的值变为5,每个包装函数内引用的i亦为5,最终逐个调用这些包装函数输出的都是5。

要让每个包装函数返回的值是各自的索引值,只需要添加一个作用域将每个包装函数与闭包分隔开,让每个包装函数引用的值来自这个作用域而不是闭包:

  1. function createFn() {
  2. var arr1 = [];
  3. for(var i = 0; i < 5; i++) {
  4. arr1[i] = (function(num) {
  5. return function() {
  6. console.log(num);
  7. }
  8. })(i);
  9. }
  10. return arr1;
  11. }
  12. var arr2 = createFn();
  13. for(var i = 0; i < arr2.length; i++) {
  14. arr2[i]();
  15. }