JavaScript | 闭包
————————————————————————————————————————————
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]());