js 预编译 解释执行 作用域链 闭包

<script>
     var a,
         b = 1;
     function c(x){
         var aa = 2;
         function d(){
            var ab = 3;
         }
    }
     var d = function(){
      //...
     }
     c(10);
    
</script>

预编译与解释执行 

页面产生时创建全局对象window对象,同时创建document、history、 location、navigator、screen等属性。

脚本文件加载完后,分析语法是否合法。

开始预编译

(1)查找变量声明,作为window属性,并且值为undefined

(2)查找函数声明,作为window属性,值为函数体

上面js预编译后的window对象:

window = {
    //页面加载创建window时,添加的属性
    a: undefined,
    b: undefined,
    d: undefined,
    c: function c(x){
          var aa = 2;
          function d(){
               var ab = 3;
           }
     }
}

预编译后,从上到下解释执行

window.b = 1;

window.d = function(){

  //...

}

执行c(10)

解释执行后的window对象:

window = {
    //页面加载创建window时,添加的属性
    a: undefined,
    b: 1,
    d: function(){//...},
    c: function c(x){
          var aa = 2;
          function d(){
               var ab = 3;
           }
     }
}

作用域与闭包(函数c的创建与执行)

在js文件预编译时,创建c函数,函数c在创建的过程中,创建了一个内部属性[[scope]],即该函数的作用域链,作用域链是作用域对象的集合。

在执行函数c时,首先预编译处理,创建一个执行上下文对象(execute context),即一个函数运行时的环境。执行上下文对象有它自己的作用域链(scope chain)。在上下文对象创建时,初始化它的作用域链。首先把c.scope赋值给context.scope,然后创建一个活动对象(Active Object),并且把活动对象放在context作用域链的最前端。

AO(活动对象)创建:

(1)查找形参和变量声明,赋值为undefined

(2)实参值传给行参

(3)查找函数声明,赋值函数体

在函数执行时,访问数据的权限,从作用域链从上往下查找。

在函数c执行时,函数内部的函数d创建,在创建过程中d有一个自己的作用域链。作用域链指向的对象与c的上下文执行环境的作用域链分别指向相同的对象。

当函数C执行完后,执行上下文对象销毁。由于AO对象被函数d的scope引用,则AO对象不能被销毁。若函数d没有被销毁(如函数d作为返回函数被全局变量引用),AO对象一直存储在内存中。

闭包:一个函数内嵌套一个函数,嵌套的函数未被释放时,外部函数内的变量不能释放。

如:

function A(){

  var a = 1;

  function B(){

    var b = 2;

       }

  return B;

}

var b = A();

分析:函数A执行后,由于函数内部的嵌套函数被全局变量b引用,即函数B不能被释放。又函数B的作用域链引用了函数A的AO对象,所以函数A内的变量的内存不能被回收。

  

原文地址:https://www.cnblogs.com/fe-huahai/p/6433270.html