闭包是指有权访问另一个函数作用域中的变量的函数。
- 如何创建作用域链(粗略)
- 函数被调用,会创建一个执行环境和作用域链
arguments
和其他命名参数的值来初始化函数的活动对象(活动对象作为变量对象)- 作用链中外部函数依次往外排
- 作用域链的作用
- 函数执行过程中,为读取和写入变量的值,需要在作用域链中逐级查找变量。
1 | 例子 |
例子如图
- 如何创建作用域链(详细)
- 全局环境的变量对象始终存在,而函数的局部环境的变量对象,则只在函数执行的过程中存在。
- 在创建函数时,会创建一个预先包含全局变量对象的作用域链,并保存在函数内部的[[Scope]]属性中。
- 在调用函数时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链。
- 函数的活动对象(在这被当作变量对象)被创建并被推入执行环境作用域的前端。
- 作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含对象
- 全局环境的变量对象始终存在,而函数的局部环境的变量对象,则只在函数执行的过程中存在。
- 常见的闭包方式:在一个函数内部创建另一个函数,如下
1
2
3
4
5
6
7
8
9
10
11function createComparisonFunction(propertyName){
return function(object1,object2){
var value1=object1[propertyName];
var value2=object2[propertyName];
if.....
}
}
var compare=createComparsionFunction("name");
var result=compare({name:"Nicholas"},{name:"Greg"});
即使内部的匿名函数被返回了,且在其他地方被调用,仍可以访问变量propertyName.
作用域链的关系如图
1.闭包与变量
闭包的问题:闭包只能取得包含函数中任何变量的最后一个值。
1 | function createFunctions(){ |
- 解释:
- 每个函数作用域链中保存着包含函数的活动对象,则都是引用同一个变量i;
- 包含函数返回后,变量i值是10,此时每个函数都引用着保存着变量i的包含函数活动对象
- 每个函数内部i的值都是10。
1 | 改进后的闭包 |
- 解释:
- 没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋值给数组。
- 在匿名函数内部,创建并返回一个访问num的闭包。
- 闭包只能取得包含函数任何变量的最后一个值。
2.this对象
this
对象是在运行时基于函数的执行环境绑定的:在全局函数中,this
等于window,而当函数被作为对象的方法调用时,this
等于那个对象。(非闭包,匿名函数情况下)匿名函数的执行环境具有全局性,因此其this对象通常指向window(除
call()
和apply()
改变执行环境外)。1
2
3
4
5
6
7
8
9
10
11var name="The window";
var object={
name:"My object",
getNameFunc:function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The window"(非严格模式下)解释:
- 每个函数在被调用时都会自动取得两个特殊变量(对象):
this
和arguments
。 - 内部函数在搜索这两个变量时,只会搜索到其活动对象为止,永远不会直接访问外部函数中的这两个变量。所以当搜不到时就等于window。
- but可以间接访问.把外部作用域中的this对象保存在一个闭包能够访问的变量中,就可以让闭包访问对象了。
- 每个函数在被调用时都会自动取得两个特殊变量(对象):
1 | var name="The window"; |
- 几种特殊的情况
1
2
3
4
5
6
7
8
9
10
11var name="The window";
var object={
name:"My object",
getName:function(){
return this.name;
}
};
object.getName(); //"My object"
(object.getName)() //"My object"
(object.getName=object.getName)() //"The window" 在非严格模式下。赋值的是函数本身,this的值不能得到维持,相当于在全局环境下调用,所以指向"window"。
3.内存泄漏
IE早些版本的问题