JavaScript 闭包

作用域链:只有内层函数可以调用外层变量,形成一条单向的作用域链

词法作用域:不同的函数之间不共享词法作用域,可以通过闭包来实现共享

作用域链与词法作用域样例

复制代码
 1 // 作用域链
 2 var a = 1;
 3 
 4 function test() {
 5     var b = 2;
 6 
 7     function test1() {
 8         // 在test1()中可以调用外层的变量a和b
 9         // 但是在外层无法调用内层的变量,形成了一条作用域链
10         var c = 3;
11         console.log(a);
12         console.log(b);
13         return b;
14     }
15     test1();
16 }
17 test();
18 
19 // 词法作用域
20 // 不同的函数之间不共享词法作用域,可以通过闭包来实现共享
21 // 在这种写法中,错误信息提示d未定义
22 // function f1() {
23 //     var d = 333;
24 //     return f2();
25 // }
26 
27 // function f2() {
28 //     return d;
29 // }
30 // console.log(f1());
复制代码

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

闭包:

  • 在正常情况下,只有function2可以访问function1中的变量var a 和var b
  • 如果想要在全局作用域中访问内层变量,就需要用到"闭包"
  • 即借function2来调用function1中的变量

优点:

  • 可以突破全局作用域链,调用函数内部变量
  • 退出后函数不会被销毁,而是保留在内存中
  • 作为迭代器来使用

缺点:

  • 闭包会使函数中的变量都保存在内存中,内存消耗很大,不能滥用闭包。在IE中可能导致内存泄漏。在退出函数之前,尽量将不使用的局部变量全部删除
  • 闭包会在父函数外部,改变父函数内部变量的值。所以如果把父函数作为对象Object使用,把闭包当作公用方法Public Method,把内部变量作为它的私有属性Private Value,此时注意不要随便改变父函数内部变量的值

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

实现形式:

  • Demo1:通过返回函数的函数形式来返回f1中的变量

  • Demo2:利用全局变量进入到函数内部来调用

闭包的基础形式

复制代码
 1 // 通过闭包突破全局作用域链
 2 
 3 // Demo1:使用返回函数的函数来实现闭包
 4 function f1() {
 5     var a = 'aaa';
 6     return function() {
 7         return a;
 8     }
 9 }
10 var a1 = f1();
11 // f1()返回的是function(){return a;}
12 console.log(typeof a1);
13 // 要打印a的值则要用a1()
14 console.log(a1());
15 // 或使用二次调用
16 var a2 = f1()();
17 console.log(a2);
18 
19 // Demo2:利用全局变量进入到函数内部来调用
20 var temp;
21 function f3(){
22     var b = 'bbb';
23     temp = function(){
24         return b;
25     }
26 }
27 // 先调用f3(),将function()赋给全局变量temp
28 f3();
29 // 执行temp()得到变量b的内容
30 console.log(temp());
复制代码

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

闭包的应用:基本形式/迭代器

复制代码
  1 // 闭包的应用
  2 // Demo1:基本应用
  3 function f1() {
  4     var n = 1;
  5     // test是全局变量
  6     test = function() {
  7         n++;
  8     }
  9 
 10     function f2() {
 11         console.log(n);
 12         return 'f2 return';
 13     }
 14     // 调用f1时返回f2()的指针
 15     return f2;
 16 }
 17 // res调用f1()时,返回f2给res
 18 var res = f1();
 19 // 打印res可以看到f2()的内容
 20 console.log(res);
 21 // 执行f2的内容可以得到n与f2()的返回值
 22 console.log(res());
 23 // 执行test()函数后n++
 24 test();
 25 // 此时调用n的值即为2
 26 console.log(res());
 27 // 说明变量n始终保存在内存中,并不会因为f1()执行结束而销毁
 28 // f1是f2()的复函数,f2又被赋予给全局变量res,f2又依赖f1,所以f1未被销毁
 29 
 30 // *******************************************************************
 31 // Demo2:设置变量访问控制,在外部不会被访问到
 32 // 全局作用域中变量getValue和setValue用于调用
 33 var setValue, getValue;
 34 // 设置自调用函数,建立初始值
 35 (function() {
 36     var num = 0;
 37     getValue = function() {
 38         return num;
 39     };
 40     setValue = function(x) {
 41         num = x;
 42     };
 43 })();
 44 // 默认得到初始值0
 45 var getNum = getValue();
 46 console.log(getNum);
 47 // 给num赋值
 48 setValue(10);
 49 // 获取获取到的值
 50 var getNum = getValue();
 51 console.log(getNum);
 52 
 53 // *******************************************************************
 54 // Demo3:迭代器效果
 55 // 对数组操作,每次调用则返回下一个元素
 56 var arr1 = ['a', 'b', 'c', 'd', 'e'];
 57 
 58 function iterator(arr) {
 59     var i = 0;
 60     return function() {
 61         return arr[i++];
 62     };
 63 }
 64 var next = iterator(arr1);
 65 console.log(next());
 66 console.log(next());
 67 console.log(next());
 68 console.log(next());
 69 console.log(next());
 70 
 71 // *******************************************************************
 72 // 迭代器的第二种形式
 73 function iterator2() {
 74     var arr2 = [];
 75     var i;
 76     // 每次循环时自调用function(x),返回function(){return x};
 77     for (i = 0; i < 3; i++) {
 78         arr2[i] = (function(x) {
 79             return function() {
 80                 return x;
 81             };
 82         })(i);
 83     }
 84     return arr2;
 85 }
 86 // 在这里next2是一个数组,数组的内容是function(){return x;};
 87 var next2 = iterator2();
 88 console.log(next2);
 89 // next2[0]执行return x;
 90 console.log(next2[0]());
 91 console.log(next2[1]());
 92 console.log(next2[2]());
 93 
 94 // *******************************************************************
 95 // 迭代器的第三种形式
 96 function iterator3() {
 97     // 闭包函数
 98     function test3(x) {
 99         return function() {
100             return x * 10;
101         }
102     }
103     var arr3 = [];
104     var i;
105     for (i = 0; i < 3; i++) {
106         // 每次循环时调用闭包函数赋给a[i];
107         arr3[i] = test3(i);
108     }
109     return arr3;
110 }
111 var next3 = iterator3();
112 console.log(next3);
113 console.log(next3[0]());
114 console.log(next3[1]());
115 console.log(next3[2]());
复制代码

 

原文地址:https://www.cnblogs.com/fanshaokun/p/7218432.html