作用域,作用域链,预编译的理解

110 阅读4分钟

我正在参加「掘金·启航计划」

预编译的理解:

预编译的步骤(AO):

  1. 查找形参和变量声明,把他的值声明为undefined
  2. 将实参的值赋给实参
  3. 寻找函数声明
  4. 执行函数

GO的执行过程:

  1. 生成了一个GO的对象
  2. 找形参和变量声明,将他作为GO属性名值为undefined
  3. 在函数体中找函数声明,值赋给函数体

AO是啥? 函数的执行期上下文(预编译时已经被定义) GO是啥? 全局的执行期上下文(未执行已经被定义)

那到底是先执行AO还是先执行GO呢? 应该是先执行全局,先生成GO,在函数执行的那一刻生成AO(预编译的过程),在找值的过程中,如果有几层嵌套关系,近的优先,先找AO,最后找GO 试着用三个案例学会预编译~

    function test(){
        return a; // 终止函数运行
        function a(){}
        var a = 2;
    }
    console.log(test())

这段代码在预编译的过程中是怎么执行的呢?

他在预编译的过程中会有一个AO对象

    AO = {
        a:undefined -> function a(){}
    }

再来 看一个例子

console.log(test())
function test(){
    a = 1;
    function a(){}
    var a = 2;
    return  a;
    
}

他的执行结果又是怎样的呢?

AO = {
    a:undefined -> function a(){} -> 1 -> 2
}
    a = 1;
    function  test(e){
        function e(){}
        arguments[0] = 2;
        console.log(e);//2
        if(a){
            var b = 3;
        }
        
        var c;
        a = 4;
        var a;
        console.log(b);//undefined 因为这里的a为false
        f = 5;//f未声明,挂载到GO上
        console.log(c);//undefinde
        console.log(a);//4
    }
    var a;
    test(1);
    console.log(a);//1 GO优先于局部的AO
    console.log(f);//5
    

他的执行结果又是怎样的呢?

    全局的GO
    GO = {
        a:undefined ->1
        test : function test(){...}
        f:5
    }
    生成GO后会生成局部的AO
    AO = {
        e: undefined ->1(实参的值赋给实参)->function e(){}->2
        b:undefined
        c:undefined
        a:undefined -> 4
        
    }

注意:预编译中找变量声明不会看代码的执行过程,比如if else等,在执行的过程中才会看变量赋值

作用域的理解

因为函数可以访问他的属性,我们可以将函数理解为 对象的一种,还记得刚开始学编程的时候有一句话印象很深刻,万物皆对象,当然对象中也有我们无法访问的固有属性,我理解为浏览器提供的方法都是固有属性

[[scope]]就是无法访问的其中一个对象,叫作用域

  1. 函数创建时,生成的一个JS内部的隐式属性
  2. 函数存储作用域链的容器,而作用域链里存储的就是AO和GO
  3. 函数执行以后AO要被销毁,每次执行函数都会生成一个新的AO(AO是一个即时的存储容器)

作用域链:当[[scope]]中所存储的执行期上下文对象的集合呈现链式结构就是作用域链

执行期上下文:当函数在执行的前一刻,会创建一个称为执行期上下文的内部对象。 一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,执行上下文被销毁。

image.png

image.png

    function a(){
        function b(){
            var bb = 23;
            aa = 0;
            b()
            console.log(aa)
        }
        var  aa = 123
        b()
        console.log(aa)
    }
    var go = 100
    a()

bb 的 AO 是拿到 aa 的 AO,就是同一个 AO,bb 只是引用了 aa 的 AO,GO 也都是同一个。function b(){}执行完,干掉的是 b 自己的 AO(销毁执行期上下文)(去掉连接线),下次 function b 被执行时,产生的是新的 b 的 AO。b 执行完只会销毁自己的 AO,不会销毁 a 的 AO。function a(){}执行完,会把 a 自己的 AO 销毁 【会把 function b 也销毁】,只剩 GO(回归到 a 被定义的时候),等下次 function a再次被执行时,会产生一个全新的 AO,里面有一个新的 b 函数。。。。。。周而复始

注意:在全局执行的前一刻(GO)函数声明已经定义,但是函数表达式并没有定义