和其它的大多数现代编程语言一样,JavaScript也采用词法作用域,也就是说,函数的执行依赖于变量作用域,这个作用域在函数定义时就决定的,而不是函数调用时决定的(很简单这样理解,只要有Function(),就会有闭包),为了实现这种词法作用域,JavaScript函数对象的内部状态不仅含有函数的代码逻辑,还必须引用当前的作用域(形象的比喻是这样的:你要想和你的意中人商量终生大事,一定要先过岳父岳母这一关,娶其中之一中者,出动全家也).函数对象可以通过作用域相互关联起来,函数体内部的变量都可以保存在函数的作用域内,这样的特性就叫闭包。
以书上两个例子:
例一:
var scope = "global"; function checkscope(){ var scope = "local"; function f(){retunrn scope;} return f(); } checkscope(); //这里会返回什么呢? 例子2 var scope = "global"; function checkscope(){ var scope = "local"; function f(){retunrn scope;} return f; } checkscope()(); //这里会返回什么呢?
经过测试两个方法执行后都会返回”local“
那总结下就是:
嵌套的函数f()定义在这个作用域链里,其中的变量scope一定是局部变量,不管在何时何地执行函数f(),这种绑定在执行f()时依然有效。
闭包可以捕捉到局部变量(和参数),并一直保存下来,看起来像这些变量绑定到了其中定义它们的外部函数。
转自:http://www.felixwoo.com/archives/247 关于闭包的微观世界:
如果要更加深入的了解闭包以及函数a和嵌套函数b的关系,我们需要引入另外几个概念:函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。以函数a从定义到执行的过程为例阐述这几个概念。
到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给c,又函数b的作用域链包含了对函数a的活动对象的引用,也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。
当函数b执行的时候亦会像以上步骤一样。因此,执行时b的作用域链包含了3个对象:b的活动对象、a的活动对象和window对象,如下图所示:
如图所示,当在函数b中访问一个变量的时候,搜索顺序是:
小结,本段中提到了两个重要的词语:函数的定义与执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤1和3)。用一段代码来说明这个问题:
function f(x) { var g = function () { return x; } return g; } var h = f(1); alert(h()); |
这段代码中变量h指向了f中的那个匿名函数(由g返回)。
如果第一种假设成立,那输出值就是undefined;如果第二种假设成立,输出值则为1。
运行结果证明了第2个假设是正确的,说明函数的作用域确实是在定义这个函数的时候就已经确定了。