JS预编译之作用域

303 阅读3分钟

我们都知道JS作为一种单线程解释性语言,它有它自己的执行机制。

JS代码执行过程分三个过程:

  1. 首先,进行语法分析
  2. JS预编译
  3. 最后解释性执行****。

那预编译中的作用域是什么意思呢?

作用域

  • [[scope]]: 每个javascript的函数都是一个对象,对象中有些属性是我们可以访问的,但有些不可以,[[scope]]这个属性就是其中之一,它只能被javascript引擎存取.
function a(){
    console.log(a.name);
    console.log(a.prototype);
}
如函数a中 a.name a.prototype 都是可以访问到的,叫做显示属性
而a.[[scope]] 访问不到的属性,叫做 隐式属性
  • [[scope]] 指的是我们说的作用域,其中储存了运行期的上下文集合

那什么是运行期的上下文集合,作用域又是如何产生的呢?

这里需要先解释俩个概念——AO、GO对象。

AO对象:Activation Object,指活动性对象,也叫执行期上下文,就是我们通常所说的作用域。这里指函数的局部作用域。

GO对象:Gobel Object,是全局对象,GO对象跟window对象是同一个对象。可以理解为window对象有两个名字 window == GO。

运行期的上下文

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

看一个例子:

  function a(){    //函数声明
      function b(){
           var bb = 234
           a = 0
      }
      var   a = 123
      b() 
      console.log(a); //0     为什么打印的是函数b里面的a?
  }
  var glob = 100
  a()           // 函数调用

函数声明->[[scope]]->scope chain ->{GO,AO}

1. 函数a分析:全局作用域下有一个a函数,有一个glob变量,a函数的调用。

  1. 在a函数声明时,会在作用域链中产生一个GO对象,对象中存在一些全局下的属性和变量。
  2. 在a函数执行时,会产生一个AO对象,并去到作用域链的顶部。

a defined a.[[scope]] -->0:GO{} //GO 全局作用域

a doing a.[[scope]] -->0:AO{} -->1:GO{} //函数自己的作用域 白板123.jpg

2. 函数b分析:1.函数a的执行带来了函数b的声明,函数b所在的是a的作用域中。 2. 函数b在执行的时候,会创建一个执行期上下文的内部对象AO

下图一是函数b刚被定义的时候,图二是函数b执行的时候,函数b的AO会来到作用域链的顶端。

04.png


03.png

结果:: 因为在查找a的时候,遵从作用域的查找变量,从作用域的顶端依次向下查找b函数的执行的时候作用于链中的AO{}和a函数执行时作用于链中的AO{}是同一个。,因为函数外部不能访问函数内部的变量,所以只有b函数执行完后,将a=0,同时b函数作用域链中的AO的a=0,函数a作用域链中的a也等于0,所以才能访问到。

作用域链(scope chain)

[[scope]]中存储的运行期的上下文集的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链

  • 查找变量:从作用域的顶端依次向下查找

最后看一个例子:

function a() {
    function b(){
        function c(){
        }
    }
}
a()
分析其作用域:
  1. a defined a.[[scope]] --> 0:GO{} a doing a.[[scope]] --> 0:aAO{} --> 1:GO{}

  2. b defined b.[[scope]] --> 0:aAO{} --> 1:GO{} b doing b.[[scope]] --> 0:bAO{} --> 1:aAO{} --> 2:GO{}

  3. c defined c.[[scope]] --> 0:bAO{} --> 1:aAO{} --> 2:GO{} c doing c.[[scope]] --> 0:cAO{} --> 1:bAO{} --> 2:aAO{} --> 3:GO{}