闭包
定义
闭包(closure)
指有权访问另一个函数作用域中变量的函数。
作用
闭包延伸了变量的作用范围。
理解闭包
闭包的产生基于以下基础。
- 执行环境(或简称环境)定义了变量或函数有权访问的其他数据,决定了它们的各自行为。
- 每一个环境都有与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。
- 全局执行环境是最外围的执行环境,而每个函数又有自己的执行环境。执行流进入一个函数时,函数的环境会被推入环境栈中。函数执行完毕后,环境栈将其弹出,将控制权还给上一个执行环境。
- 代码在一个环境中执行时,会创建变量对象的一个作用域链。其用途是保证对执行环境有权访问的所有变量和函数的有序访问。
- 标识符解析是沿着作用域链一级一级地搜索标识符的过程。从前端开始,一直延续到全局执行环境。
- 作用域链的前端始终是当前执行代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。
- 函数的活动对象最开始只包含
arguments
对象,第二个变量对象来自包含(外部)环境,第三个是包含环境的包含环境……直到包含环境为全局环境为止。 - 普通函数创建时,会创建一个包含全局作用域的作用域链。这个作用域链被保存在内部的
[[Scope]]
属性中。 - 普通函数调用时,会为函数创建一个执行环境,通过复制函数的
[[Scope]]
属性中的对象构建执行函数的作用域链。 - 在一个函数内部定义的函数会将包含函数的活动对象添加到它的作用域链中。包含函数执行完毕后,其执行环境会被销毁,但活动对象仍保存在内存中,因为内部函数的作用域链仍然在引用这个活动对象。直到内部函数被销毁后,包含函数的活动对象才会被销毁。
this指向的问题
- 观察下面的代码:
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return function() {
return this.name;
};
}
};
console.log(object.getNameFunc()());
上面最后一行的object.getNameFunc()()
相当于:
var f = function() {
return this.name;
}
f();
故上面的this
指向window
,且无闭包产生。
- 继续观察代码:
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};
console.log(object.getNameFunc()())
这里使用局部变量保存object
的this
,而内部返回函数中使用到了that
,产生闭包,that.name
访问的即是object
内的name
局部变量。