闭包
在ECMAScript语言里,函数是第一类对象。这就是说函数是可以像参数一样被传递给别的函数(这种情形,他们称为"funargs",简称"functional
arguments")。
函数接受了函数参数是称为高阶函数或,更接近于数学上,操作符。函数也可能从别的函数返回出来。函数从别的函数返回,那么就称为函数的值函数。
关于函数参数和函数值有两个概念问题。这两个子问题被称为泛函参问题。要准确的解决这个泛函参问题,就引入了闭包概念。然我们详细的描述这
两个问题(我们可以看到,在ECMAScript中使用了函数的[[Scope]]属性来解决这个问题)。
第一个子问题是向上查找参数问题。它出现在当一个函数被返回到外面时,并且使用了自由变量,这个问题就会出现。
甚至于外部上下文结束之后,为了能访问到外部的上下文变量,内部函数在创建的同时就会保存在内部函数的[[Scope]]属性的父元素的作用域中。
当这个函数被激活时,这个上下文的作用域链就由当前激活对象和它的[[Scope]]属性(确切的说,这就是我们所说的):
Scope chain = Activation object + [[Scope]]
可以看到重要的事情——在创建时候——一个函数保存了外部函数的作用域, 这是为了将来函数调用时,在查找变量时,要用到这个作用域链。
function foo(){ var x = 10; return function bar(){ console.log(x); }; } //"foo" returns also a function //and this returned function uses // free variable "x" var returnedFunc = foo(); //global variable "x" var x = 20; //execution of the returned function returnedFunc(); //10
这种作用域的类型称作静态作用域。我们看到变量x是在返回的bar函数的[[Scope]]里查找到的。理论上,在上面的例子,
当变量x被设置为20而不是10,这是一个动态作用域。然而,动态作用域是不会使用的在ECMAScript。
第二个子问题是向内查找函数的参数问题。这种情况,外部上下文可能存在,但是对于解释标识符会产生歧义。问题是:
哪个作用域里的标识符的值会被使用——是在函数创建时还是在函数执行时产生的作用域呢?为了避免二义性与确立闭包,静态作用域会被使用: