1. 闭包
有权访问另一个函数作用域中变量的函数,常见创建闭包的方式是在一个函数内部创建另一个函数。
2. 闭包原理
先看看普通函数执行过程中,如何在作用域链查找变量的:
1 function compare(val1, val2) { 2 if(val1 < val2 ) { 3 return -1; 4 } else if (val1 > val2) { 5 return 1; 6 } else { 7 return 0; 8 } 9 }
10 var result = compare(5,10);
compare函数在执行过程中的作用域链:
简单的闭包函数:
1 function createComparesionFunction(propertyName) { 2 return function(object1, object2) { 3 var val1 = object1[propertyName]; 4 var val2 = object2[propertyName]; 5 if(val1 < val2) { 6 return -1; 7 } else if (val1 > val2) { 8 return 1; 9 } else { 10 return 0; 11 } 12 } 13 }
14 var compareNames = createCompareFunction("name");
15 var result = compareNames({name: 'frank', name: 'jack'});
16 compareNames = null; (解除引用,释放内存)
注意: 由于闭包会包含它的函数的作用域,因此比其他函数占用内存要多,所以只有在必要的时候再使用
3. 闭包应用场景
(1) 外部函数读取内部函数变量
1 function f1() { 2 var n =99; 3 function f2() { 4 alert(n); 5 } 6 return f2; 7 } 8 9 var result = f1(); 10 result();//99
(2) 将变量一直保存到内存中
1 function f1() { 2 var n = 99; 3 nAdd = function() {n +=1}; 4 function f2 () { 5 alert(n); 6 } 7 return f2; 8 } 9 10 var result = f1(); 11 result();//99 12 nAdd(); 13 result();//100
分析: result是f2闭包函数, 调用两次闭包函数,第一次输出是99,第二次输出是100. 可以看出局部变量并没有因为f1()函数执行之后,而从内存中清除掉,而是一直保存在内存中。原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。首先在nAdd前面没有使用var关键字,因此 nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
4. 使用闭包注意事项
(1). 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
(2). 闭包在父函数外面,可以改变父函数内部的变量,如果将父函数作为对象,闭包作为公共方法,变量为私有属性,这是要注意不要随便改变父函数中的变量