引入定义:闭包只有权访问另一个函数中的作用域中的函数。
简单点说,就是当某函数a执行完毕后,闭包不会使得GC(JavaScript的回收机制)去回收a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量。
代码示例:
window.onload = function(){ function createComparisonFunction(propertyName){ return function(object1, object2){ var value1 = object1[propertyName]; var value2 = object2[propertyName]; console.log(arguments); if(value1 < value2){ return –1; }else if(value1 > value2){ return 1; }else{ return 0; } }; } var compareNames = createComparisonFunction("name"); var result = compareNames({name:"Nicholas"},{name:"Greg"});
在此例中,当调用玩createComparisonFunction函数后,局部活动对象,即
调用createComparisonFunction这个函数是产生的活动对象理应被销毁,但是如我们在下面调用的语句,compareNames依然成功调用,而注意到,我们在匿名函数内(3行)是引用了createComparisonFunction的西参数的,其实这就是闭包的一个细微体现吧。
不信,咱再证明一下无妨:在上面代码的第五行,不是有一个控制带输出语句吗,其打印的是匿名函数的匿名参数arguments,结果附上:
从上图中,我们不难看出,在匿名函数的<function scop>中,保存有作用域链,其中就有createComparisonFunction的形式参数propertyName:“name”。
闭包注意事项1: 闭包与变量:
在闭包中的变量有其特殊性,但其实深究一下,也其实挺普遍的:
上代码:
function createFunctions(){ var result = new Array(); for(var i = 0; i < 10; i++){ result[i] = function(){ return i; }; } return result; } var results = createFunctions(); for(var i = 0; i < 10; i++){ alert(results[i]()); }
最终输出结果: 每一次输出都为10
解释:而当createFunction()函数返回后,变量i的值是10,此时每一个函数都引用着保存变量i的同一个变量对象,所以……
当然,我们可以通过一个非常巧妙的方法来解决这个问题:
function createFunctions(){ var result = new Array(); for(var i = 0; i < 10; i++){ result[i] = function(num){ //用num来保存i的初始值 return function(){ return num; } }(i); } return result; } var results = createFunctions(); for(var i = 0; i < 10; i++){ console.log(results[i]()); //0,1,2,3,4,5,6,7,8,9 }
方法之妙,不言而喻,也许这就是语言之妙吧。
说完闭包,我们再来谈谈与其密切相关的东西,作用域链和活动对象:
官方: 当某个函数被调用时,会创建一个执行环境及相应的作用域链,然后,使用arguments和其它命令参数的值来初始化函数的活动对象,但在作用域中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象始终处于第三位,知道作用域终点的全局执行环境。
嘛意思呢:哥总结为下:
1:函数一创建,即产生作用域链。
2:作用域链保存在arguments或其他参数中(见本文上面的截图)。
3:何为作用于链:
引入作用域:任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。
作用域链:即为作用域的集合。其本质上是一个指向变量对象的指针列表,它只引用但不包含变量对象。
上代码: test1:
var name = "The Window"; var num = 100; // 全局变量对象 var object = { name: "Ny Object", //活动对象开始-------本地活动对象------------------ getNameFunc: function(num){ return function(){ return num; }; } // 活动对象结束:------------------------------------------------------------------------ }; console.log(object.getNameFunc(500)()); //500
test2:
var name = "The Window"; var num = 100; // 全局变量对象 var object = { name: "Ny Object", //活动对象开始-------本地活动对象------------------------------------------------- getNameFunc: function(num){ return function(num){ return num; }; } // 活动对象结束:------------------------------------------------------------------------ }; console.log(object.getNameFunc(500)(50)); //50
test3:
var name = "The Window"; var num = 100; // 全局变量对象 var object = { name: "Ny Object", //活动对象开始-------本地活动对象------------------------------------------------- getNameFunc: function(){ return function(){ return num; }; } // 活动对象结束:------------------------------------------------------------------------ }; console.log(object.getNameFunc()()); //100
从上面三个代码的测试结果不难看出,本地活动对象优先,然后一级一级的网上寻找目标变量(当函数要访问某个变量时)。
其完整的寻找路径(最长的时候)即为一个作用域链。