论坛首页 Web前端技术论坛

Javascript的作用域,闭包的真正含义

浏览 2993 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-08-06   最后修改:2012-08-06

javascript函数作用域分两个阶段
一个叫创建时阶段
一个叫运行时阶段

定义1:
所谓“创建时阶段”就是一个函数被以某种语法定义出来的时候,
并且此函数被创建在一个运行时阶段的作用域中时,此函数才为闭包状态,才能有自己的“创建时阶段”的作用域。

(在这里要说的是,所有被压入函数作用域链的值,都是对“可变对象”的引用,当所有这个“可变对象”的引用都断开的时候,这个“可变对象”才可能被垃圾回收,从内存中删去)

当浏览器的一个窗口被打开时,就自动开启了一个最顶级的"运行时阶段的作用域",
我们一般叫它“全局作用域”,所有在这个“全局作用域”中以某种语法定义出来
的函数就自动进入了“创建时阶段”,就有了自己“创建时阶段”的作用域。
并且自己“创建时阶段”的作用域链上自动插入,
创建自己的“运行时阶段”的“可变对象”的引用,
并且逐级插入。。。直道插入最后一个“可变对象”(就是最顶级的“运行时阶段”所产生的,一般我们叫它‘Global Object’).

定义2:
所谓“运行时阶段”就是一个函数被()操作符标识为要运行的时候,
(提示所有被()操作符标识为运行的函数都要在一个“运行时阶段”的作用域中才能被运行,如果不在则无法被运行)
此时,函数本身会创建一个内部对象,叫“运行期上下文”对象,
“运行期上下文对象”有自己的[[scope]]属性,此属性将copy此函数“创建时阶段”的[[scope]]属性的值,也就是“创建时阶段的作用域链”做为“运行时阶段的作用域链”的最初形态,有了这个最初形态以后,运行期上下文将组织聚集函数内部的所有标识符等等属性为一个对象,这个对象叫做"可变对象",然后将这个对象的引用压入自己作用域链的第一个位置。



下面做代码实例解析

var a = function() {
 var i = 0;
 var b = function() {
   alert(i);
 };
 return b;

}


以上代码,此时a函数被以某种语法定义出来
a函数被创建在最顶级的“运行时阶段”的作用域链中,
此时a函数才被允许有自己的“创建时阶段”的作用域链。。。
此时a函数的作用域链上插入了创建自己的“运行时阶段”的“可变对象”的引用,
就是对"Global Object可变对象"的引用

此时b函数以某种语法定义出来,并且是被定义在a函数内部,
但由于a函数的作用域才处于“创建时阶段”
如果b函数要有自己的作用域就必须符合定义1
但很可惜不符合。。。因为b函数没有被创建在一个“运行时阶段”的作用域中,而是被创建在一个“创建时阶段”的作用域中,不满足定义1,所有b函数此时还没有作用域。

当执行以下这一行代码时

var b = a();


a函数被()操作符标识为要运行的时候,
此时,a函数本身会创建一个内部对象,叫“a函数运行期上下文-01”的对象,
(这里提示一下,每当a函数被运行的时候都会产生一个"a函数运行期上下文-XX"对象)
“a函数运行期上下文对象”有自己的[[scope]]属性,此属性将copy a函数“创建时阶段”的[[scope]]属性的值,也就是“创建时阶段的作用域链”做为“运行时阶段的作用域链”的最初形态,有了这个最初形态以后,运行期上下文将组织聚集函数内部的所有标识符等等属性为一个对象,这个对象叫做"可变对象",然后将这个对象的引用压入自己作用域链的第一个位置。

此时b函数终于符合定义1了,它被创建在一个“运行时阶段”的作用域链中,于是b函数有了自己的“创建时阶段”的作用域,并且“创建时阶段”的作用域链上插入a的可变对象的引用和全局可变对象的引用,此时b函数处于闭包状态。

当执行以下这两行代码时

var b = a();
b();


当第一行代码执行完毕的时候,也就是当a函数执行完毕的时候,“a函数的运行期上下文对象”也被销毁,所以此上下文创建的作用域链也销毁,所以对"a的可变对象"的引用断开了,也就是说,如果“a的可变对象”如果没有其他引用的话随时会被垃圾回收释放内存,但是上面说了b函数处于闭包状态了,b函数的“创建时阶段”的作用域链已经有了一个对"a的可变对象"的引用,也就是说不会被垃圾回收无法释放内存,直到b函数执行完毕后“a的可变对象”和“b的可变对象”才都断开了引用,才会被垃圾回收释放内存。



读了还不懂作用域和闭包的同学,自己买一款豆腐去死吧


总结一下:

任何一个函数当它拥有自己的“创建时阶段”的作用域时,它的“创建时作用域链”上
都持有对它外部每一层函数的可变对象的引用...从而导致闭包状态

函数嵌套的越深其需要的内存空间越大,因为外部每一层函数的可变对象都被其引用,导致无法释放
如果内部第99层函数用到了内部第3层函数的某一个变量,将导致其作用域链的大规模搜索,从而导致性能问题。。。



我这篇文章不一定对,因为还有一个问题没有得到正解
就是如果内部函数没有用到外部函数的数据的时候,是否会对外部函数的可变对象持有引用

我的文章里说的观点是不管有没有用到外部函数的数据,都对外部函数的可变对象持有引用。

这个问题就变的很复杂了
是因为用了外部的函数的数据会形成了引用外部函数的可变对象
还是因为持有外部函数的可变对象的引用才能访问外部函数的数据。
   发表时间:2012-09-24  
可变对象这是啥东东?变量吗?

内部函数是否持有引用(我理解你的意思是指内部函数的execution context中是否存在外部函数的Acitvation object引用),这个与是否使用外部函数的变量无关的,内部函数在执行过程中都会有个[[scope]] list指向它的execution context,而这里面是必然包含外部函数的Acitvation object的一直到Global object。

推荐楼主去看下:http://jibbering.com/faq/notes/closures/


0 请登录后投票
   发表时间:2013-02-01  
js函数调用分成两个阶段是符合ecma标准的,这么说没错,不过文章这么多专业词汇好像有胡乱翻译的嫌疑,容易让读者不知所云。想深入了解js函数的执行机制,直接去读ecma的文档最清楚。
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics