论函数初次执行时的作用域链及变量对象

再次回顾闭包这个章节,又一次讲到函数的作用域链以及变量对象,谈谈这一次的理解。
所谓闭包,就是在一个函数内部,定义另一个函数。内部函数可以访问包含函数(外部函数)的中变量,这就是闭包。闭包这个词,很形象地描述了这个定义。
正常函数在执行时,funciton F(){} F();,
会创建作用域链,当前的活动对象,会被推入作用域链的顶端,其外部函数也就是全局对象会被放在作用域链里的第二个位置。活动对象中会包含函数中定义的变量以及arguments和this。所以,函数F在执行之后,它会有一个内部属性[[Scope]]被赋给函数,该内部属性就是初始化好的作用域链。当函数执行完毕之后,作用域链以及当前的活动对象会被销毁。这就是一个函数执行的生命周期。
但是当函数中引用了包含函数的变量后,情况就不是这样了。例如,function F(){var i = 0; return function(){return i;}} f = F(); f();。
我们可以看到函数F内部返回了一个匿名函数,当执行函数F后,变量f指向该匿名函数,执行该匿名函数后,返回值为函数F里定义的变量i。而且如果f不被销毁,那么函数F中的变量对象,也不会被销毁。这就存在一个问题,如果大量定义类似于F这样的函数,那么如果不销毁那些匿名函数,内存的占用的将会变的越来越多。为什么会这样?让我来详细阐述一下,函数在第一次执行过程中的作用域链及变量对象的变化。
当函数F被执行后,其内部的活动对象会被初始化, i就是其中之一,而this以及arguments因为没有使用,所以不做讨论。接下来返回一个匿名函数,匿名函数的作用域里,有对包含函数F的引用,它引用F里的变量i。当定义一个函数时,会把包含函数的变量对象添加到该定义的函数作用域中的第一位,包含函数的包含函数的变量对象放到作用域链中的第二位,以此类推,作用域链的尾端始终是全局变量,而作用域链保存在定义的函数的[[Scope]]属性中。而当执行该定义的函数之后,会创建当前的活动对象,并且把该活动对象作为变量对象,推入该函数的内部属性[[Scope]]中的第一位,也就是作用域链的顶端。于是查找变量就变得相当容易,首先根据变量名称,从作用域的第一位,找到作用域的最后一位,直到找到为止,如果没有找到该变量名,就报错。函数F虽然执行完了,它的作用域链也被销毁,但是它的变量对象依旧保存在内存中,因为它被匿名函数f的作用域链所引用。而所谓的作用域链,无非就是一个指向变量对象指针的列表。变量对象就是一个内存块,保存着各种属性和方法。而活动对象是指当前活动的变量对象。
这就是我所了解的函数初次执行中,变量对象和作用域的变化情况。

avatar

chilihotpot

You Are The JavaScript In My HTML