JavaScript闭包底层解析
1. 闭包是一个函数,这个函数有权访问另一个函数作用域中的变量,创建闭包最常见的方式,就是在函数内部创建函数。要想彻底搞清其中细节,必须从函数从创建到调用的时候都发生了什么入手
2. 函数第一次被调用,创建一个执行环境,和相应的作用域链
3. 作用域链赋值给一个特殊的内部属性Scope
4. 使用this,arguments和其他命名参数的值初始化函数的变量对象
5. 在作用域链中,内部函数的活动对象处于第一位,外部函数的活动对象始终处于第二位……直至全局执行环境
6. 执行函数时,为读取写入变量的值,需要在作用域链中查找变量
function compare(a ,b){ if(a<b){ return -1; }else if(a>b){ return 1; }else{ return 0; } } var result = compare(5,10); // -1 1. 第一次调用compare()函数时,会创建一个活动对象,包括{this,arguments ,a,b} 2. 全局执行环境的变量对象{compare , result, this},在compare()的作用域链中处于第二位的
7. 每一个执行环境都有一个用来存放变量的对象——变量对象
1. 全局环境中的变量对象始终存在
2. compare函数的局部环境的变量对象,只在函数执行过程中存在
8. 在创建compare函数时,先创建一个作用域链,这个作用域链预先就包含了全局变量对象
9. 作用域链保存在compare函数内部的Scope属性中
10. 调用compare函数时,会为函数创建一个执行环境,compare函数的局部变量对象被创建并放到作用域链的前端。
11. 所以,作用域链本质上是一个指向变量对象的指针列表,只引用但不实际包含变量对象
1. Scope[{this,arguments ,a,b} , {compare , result, this}]
2. compare函数在访问一个变量时,先从scope中第一个元素开始搜索,一个一个往后找
3. 函数执行完毕后,局部变量对象被销毁,内存中仅存全局变量对象
1. 存在闭包就不一样了
2. 在函数内部定义的函数(子函数)会将外部函数(父函数)的变量对象添加到它(子函数)的作用域链中
function createCompareFunction(propName){ return function(obj1 , obj2){ var a = obj1[propName]; var b = obj2[propName]; if(a<b){ return -1; }else if(a>b){ return 1; }else{ return 0; } } } var compare = createCompareFunction('name'); var result = compare({name:"Nico"} , {name:"Greg"});//true
1. 在内部的匿名函数的作用域链中,实际上包含外部函数的变量对象 2. 这个内部的匿名函数被return到外部后,这个匿名函数它的作用域链被初始化了 3. 包含createCompareFunction函数的变量对象以及全局变量对象 4. 所以,匿名函数可以访问createCompareFunction函数中所有的变量 5. 更为重要的是,createCompareFunction函数执行完毕被销毁后,其变量对象还有一份保存在匿名函数中,所以仍可以访问其内部的变量