闭包(Closure)
闭包是JavaScript一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量.
因为函数是JavaScript中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数.
作用域的好处是内部函数可以访问定义它们的外部函数的参数和变量(除了this和arguments).
var myObject = {
value:0,
increment:function(inc){
this.value += typeof inc === 'number' ? inc : 1;
}
}
和以字面量形式去初始化myObject不同,我们通过调用一个函数的形式去初始化myObject,该函数会返回一个对象字面量.
函数里定义了一个value变量.该变量对于increment和getValue总是可用的,但函数的作用域使得它对其他程序来说是不可见的.
var myObject = (function(){
var value = 0;
return {
increment:function(inc){
value += typeof inc === 'number' ? inc : 1;
},
getValue:function(){
return value;
}
}
})();
函数可以访问它被创建时所处的上下文环境,这被称为闭包.
var que = function(status){
return {
get_status:function(){
console.log(status);
}
}
}
var myQue = que("amazed");
myQue.get_status();
这个que函数被设计成无须在前面加上new来使用,所以名字也没有首字母大写.
当我们调用que函数时,它返回包含get_status方法的一个新对象.
即使que已经返回了,但get_status方法仍然可以访问que对象的status属性.
get_status方法并不是访问该参数的一个副本,它访问的就是该参数本身.
javaScript 闭包的实现:
如开篇所说, JavaScript 中的闭包实现与 JavaScript 的 Scope Chain 是密不可分的. 首先在 JavaScript 的执行中会一直存在一个Execute Context Stack
,
Execute Context Stack
中最下面一个一定是GlobalContext
, 而在每一个函数的执行开始就会向这个 stack 中压入一个此 Function 的Execution Context
;
而一个Execution Context
的组成分为三部分:
Variable Object
: 存储方法内的变量 vars, 方法传入的参数, 函数内定义的函数等等(函数表达式不保存),Scope Chain
: 这个函数执行的时候用以寻找值的 Scope Chain, 这个 Scope Chain 由 Variable Object + All Parent Scopes 组成, Variable Object 会放在这个 Scope Chain 的最前面, 这也是为什么函数内的变量会被最先找到;thisValue
, 函数被调用的时候的 this 对象, 存储的就是函数的调用者(caller)的引用;
对于 Variable Object 在不同的情况下会有不同的定义, 例如在全局的时候被称为 Global Object, 而在函数中则被称为 Activation Object 激活对象;
正是由于有了 Execution Context 中的 Scope Chain, JavaScript 才能够使得在方法 bar()
的内部访问到方法 foo() 中的变量 a, 才能够使方法 bar() 将变量 a 关闭在自己的作用范围内不让他随 foo() 方法的执行完毕而销毁;