一般谈到js中的作用域,则跨不过function这个对象,js的函数都表示为一个对象,具体说就是Function的一个实例。它与其他对象一样,可以进行属性的增加删减等
var f = function(){};
f.a = 1;
这里的a就是可以被直接访问的属性,当然f
有一些不能被外部仅供js引擎存取的内部属性,其中一个内部属性为[[Scope]]
,这个内部属性包含了一个函数被创建的作用域中对象的集合。这个集合被称为函数的作用域链,它决定了哪些数据可以被函数访问。当一个函数创建后,它的作用域链会被创建此函数的作用域中可访问的数据对象所填充。
function a(num1,num2){
var sum = num1 + num2;
return sum;
}
此函数在全局的环境中创建,则在创建的时候其的作用域链就已经被入栈了一个对象,该对象指向的便是所有的在全局中定义的变量。
在函数a执行的时候会创建一个被称为执行环境的内部对象,一个执行环境定义了一个函数执行的时候的环境。每次执行函数都会产生一个对应的执行环境,每次函数执行完毕其执行环境便会被销毁。
当执行环境被创建的时候,其作用域链初始化为当前运行函数的[[Scope]]属性中的对象。在函数内部出现的值按照它们出现在函数中的顺序被复制到执行环境中的作用域链中,一旦此过程被完成,则一个称谓活动对象的新对象就被创建好了,此对象包含了所有局部变量,命名参数,参数集合以及this,然后该活动对象被插入了作用域链的最前端,当执行环境消失的时候,该活动对象也随之消失。
在执行的时候,参数寻找便按照执行环境的作用域链进行寻找,而this只会寻找到其活动对象结束为止。
关于闭包:
上图中Closure是assignEvents的一个闭包,当assignEvents执行的时候,其执行环境如其指针所示,其栈顶端是活动对象,然后是其[[Scope]]对象的复制。
而闭包Closure在被创建的时候,其环境便是assignEvents的执行环境,因此它的作用域中便会将assignEvents执行时候所有的对象存储下来。
当其自己执行的时候,执行环境被创建,则它的顶端是其自有的活动对象,然后便是其复制OK的作用域(包含了assignEvents的活动对象)。
因此这便是闭关能够访问其父方法内部对象的原理。
闭包的[[Scope]]中会有assignEvents活动对象的引用,因此原本应该被销毁的assignEvents的活动对象便无法被销毁,由此闭包需要更大的内存开销([[Scope]]的增大以及执行环境被创建时候内存开销的增大),会导致内存泄漏。