原文连接----请点这里javascript
闭包是指有权访问另外一个函数做用域中的变量的函数,建立闭包的常见方式,就是在一个函数内部建立另外一个函数。
之因此一个内部的函数能够访问其外部的变量,并且在其被返回或是调用时还能够访问,是由于这个内部函数的做用域链中包含外部函数的做用域。前端
在了解闭包以前,先要熟悉如下几点:
1. 首先要理解执行环境,执行环境定义了变量或函数有权访问的其余数据。
2. 每一个执行环境都有一个与之关联的变量对象,环境中定义的全部变量和函数都保存在这个对象中。
3. 每一个函数都有本身的执行环境,当执行流进入一个函数时,函数的环境就会被推入到一个环境栈中。而在函数执行以后,栈将其环境弹出,把控制权返回给以前的执行环境。<!-- more -->
4. 当某个函数被调用时,会建立一个执行环境及其相应的做用域链。而后使用arguments
和其余命名参数的值来初始化函数的活动对象。在函数中,活动对象做为变量对象使用(做用域链是由每层的变量对象链起来的)。
5. 在做用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,直到做用域链终点即全局执行环境。
6. 做用域链的本质是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。java
不谈论闭包,通常的,从在全局执行环境建立一个函数开始。
在建立一个函数时,会建立一个预先包含全局变量对象的做用域链,这个做用域链被保存在函数内部的[[Scope]]
。
而后执行流进入这个函数,函数的执行环境被压入环境栈中,此函数执行环境的活动对象做为变量对象被建立并推入执行环境做用域链的前端。
对这个例子中的函数而言,其做用域链中包含两个变量对象:本地活动对象和全局变量对象。
不管在何时在函数中访问变量时,会从做用域链搜索变量名。
通常状况下,函数执行完,局部活动对象就会被销毁,内存中仅有全局做用域(里边只有全局执行环境的变量对象)。
如下面这段代码为例:安全
function compare (value1, value2) { //建立一个预先包含全局变量对象的做用域链,保存在[[Scope]] if (value1 < value2) { //访问函数变量时,即在代码最后一条语句执行过程当中,会从做用域链前端开始搜索变量名 return -1; } else if (value1 > value2) { return 1; } else { return 0; } } var result = compare(5, 10); //执行流进入函数时,compare的执行环境压入环境栈 //compare执行环境的活动对象做为变量对象接到做用域链的前端 //函数执行完,compare执行环境弹出栈,compare活动对象销毁
如图,做用域链从0开始向后查找:闭包
以下是一个以属性名做为参数,按其属性的值对数据进行排序的函数:函数
function createComparisonFunction(propertyName) { return function(object1,object2){ //返回一个匿名函数 var value1=object1[propertyName]; var value2=object2[propertyName]; if(value1<value2){ return -1; } else if (value1>value2){ return 1; } else { return 0; } }; } var data=[{name:"Zachary",age:28},{name:"Nicholas",age:29}]; data.sort(createComparisonFunction("name")); console.log(data[0]); //Object {name: "Nicholas", age: 29} data.sort(createComparisonFunction("age")); console.log(data[0]); //Object {name: "Zachary", age: 28}
createComparisonFunction()
函数和返回的匿名函数的做用域链以下图所示: spa
在匿名函数从
createComparisonFunction()
中被返回后,它的做用域链被初始化为包含createComparisonFunction()
函数的活动对象和全局变量对象。这样,匿名函数就能够访问在createComparisonFunction()
中定义的全部变量。指针
更为重要的是:code
createComparisonFunction()
函数在执行完毕后,其余活动对象也不会被销毁,由于匿名函数的做用域链仍然在引用这个活动对象。对象
当createComparisonFunction()
函数返回后,其执行环境的做用域链会被销毁,但它的活动对象仍然会留在内存中;直到匿名函数被销毁,createComparisonFunction()
的活动对象才会被销毁。
例如如下代码,返回的匿名函数被保存在变量compareNames
中,经过将compareNames
设置为null
来解除对匿名函数的引用,解除引用以后垃圾回收例程将会清除该匿名函数,随之该匿名函数的做用域链也会被销毁,则其做用域链上的其余做用域也会安全的销毁(全局做用域除外)。
var compareNames = createComparisonFunction("name"); var result = compareNames({ name: "Nicholas" }, { name: "Greg" }); compareNames = null;