从底层看JS执行机制

从一个简单的例子出发

先从一个简单的例子出发(先不涉及异步),看看本身是否大体了解浏览器的执行机制:html

console.log(a);
var a=1;
function foo(a){
    console.log(a);
    var a=2;
    console.log(a);
}
foo(a);
执行结果:

undefined 1 2git

若是你的预测结果不同,那你能够看看下面几个常见的误区:github

  • var a=1不是进行了变量提高?为何第一个打印的结果为 undefined?
答:变量提高只提高变量的声明,并不进行赋值。其中变量提高发生在预编译阶段,此时a=undefined,预编译结束后代码以下
//函数声明和变量声明进行提高,且函数声明优先级更高
function foo(a){
    console.log(a);
    var a=2;
    console.log(a);
}
var a;
console.log(a);
a=1;
foo(a);

很明显第一个结果为undefined。web

  • foo函数中参数名和变量名都为a,使用时该以哪个a为准?
变量声明在顺序上跟在函数声明和形式参数声明以后,同时,若是变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。例子中的var a=2,能够拆分为var a;a=2;其中a=2是对参数a进行赋值。

如今咱们详细地说一说js的执行机制:

首先你须要理解以下几个概念:数组

JavaScript中的堆和栈

  • 栈(stack) 栈stack为自动分配的内存空间,它由系统自动释放;
  • 堆(heap) 堆heap是动态分配的内存,大小不定也不会自动释放。
JavaScript 中的变量分为基本类型和引用类型。其中,基本类型存在于栈中,引用类型存在于堆中。在js的执行阶段,当执行到a=2这样的赋值语句时,js引擎线程会先判断2是基本类型仍是引用类型,若是它是基本类型,则直接对执行栈中的AO进行赋值a=2(AO会在下面的执行上下文中讲到),如果引用类型,则在堆中存入2,而后用2在堆中的地址对AO进行赋值。

执行环境

js的执行环境分为三种:浏览器

  • 全局环境(JS代码加载完毕后,进入代码预编译即进入全局环境)
  • 函数环境(函数调用执行时,进入该函数环境,不一样的函数则函数环境不一样)
  • eval(不建议使用,会有安全,性能等问题)

js每进入一个执行环境就会建立一个执行上下文,并将它放入执行栈中。执行上下文会在下文讲到。安全

单线程(同步和异步)

js是一门单线程语言,但并不意味着参与js执行过程的线程就只有一个。一个有四个线程参与该过程:
JS引擎线程、事件触发线程、定时器触发线程、HTTP异步请求线程。其中,只有JS引擎线程在执行JS脚本程序,其余三个线程只负责将知足触发条件的处理函数推动事件队列,等待JS引擎线程执行。异步

举一个简单的例子来讲:函数

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

console.log('script end');
  1. JS引擎主线程按代码顺序执行,当执行到console.log('script start');,JS引擎主线程认为该任务是同步任务,因此马上执行输出script start,而后继续向下执行;
  2. JS引擎主线程执行到setTimeout(function() { console.log('setTimeout'); }, 0);,JS引擎主线程认为setTimeout是异步任务API,则向浏览器内核进程申请开启定时器线程进行计时和控制该setTimeout任务。因为W3C在HTML标准中规定setTimeout低于4ms的时间间隔算为4ms,那么当计时到4ms时,定时器线程就把该回调处理函数推动任务队列中等待主线程执行,而后JS引擎主线程继续向下执行;
  3. JS引擎主线程执行到console.log('script end');,JS引擎主线程认为该任务是同步任务,因此马上执行输出script end;
  4. JS引擎主线程上的任务执行完毕(输出script start和script end)后,主线程空闲,则开始读取任务队列中的事件任务,将该任务队里的事件任务推动主线程中,按任务队列顺序执行,最终输出setTimeout,因此输出的结果顺序为script start script end setTimeout;

若是还不清楚,能够看看下图:性能

图片描述
首先,这是一个浏览器环境,其中主线程操做堆和执行栈,而RunTime中存在着许多web API,当主线程读取到setTimeOut等API时,它会交给其余线程来处理(setTimeOut则是定时器触发线程),定时器触发线程会先将setTimeOut中的回调函数存放在event table中,当知足触发条件时(如上面的4ms),就将回调函数推入事件队列(callback queue)中,等待主线程空闲(执行栈中为空),回调函数则被推入执行栈中进行执行。

执行上下文

执行上下文可理解为当前的执行环境,与该运行环境相对应。js引擎每进入一个环境就会建立相应的执行上下文,建立执行上下文的过程当中,主要作了如下三件事件,如图:

图片描述

其中,变量对象VO(Variable object)用于存放声明后的变量、函数和形参。咱们举一个例子来讲:

var a = 10;
 
function test(x) {
  var b = 20;
};
 
test(30);

对应的变量对象是:

// 全局上下文的变量对象
VO(global) = {
  a: undefined,
  test: <reference to function>
  //<reference to function>是test函数位于堆中的地址
};
 
// test函数上下文的变量对象
VO(test) = {
  arguments: {
      x:undefined,
      length:1
  },
  b: undefined
};

当预编译结束,js进入解释执行阶段时,VO就会转化为AO(Active object),也就是活动对象。AO中变量和参数的值再也不是undefined,它们的值会随着js的逐步执行而发生变化。

做用域链用于代表上下文的执行顺序。上例中的做用域链为:

scopeChain: [VO(test),  AO(global)],
  • 咱们这里直接使用数组表示做用域链,做用域链的活动对象或变量对象能够直接理解为做用域;
  • 它的第一项永远是当前做用域(当前上下文的变量对象或活动对象);
  • 最后一项永远是全局做用域(全局执行上下文的活动对象);
  • 做用域链保证了变量和函数的有序访问,查找方式是沿着做用域链从左至右查找变量或函数,找到则会中止查找,找不到则一直查找到全局做用域,再找不到则会抛出引用错误。

this指向当前做用域。这里不作过多分析。

执行的三个阶段

js的执行分为三个阶段:

  1. 语法分析阶段:<script>标签加载即开始语法分析,分析整个标签内的语法错误。无错误即进入预编译阶段。
  2. 预编译阶段: 每进入一个新环境,就进行一次预编译,同时建立一个执行上下文(包含VO对象、做用域链、this,忘了是什么就往回看看)并放入执行栈中。此时,当前环境会进行必定的函数提高和变量提高,注意:函数提高优先于变量提高。环境中没有新的函数声明则进入解释执行阶段。
  3. 解释执行阶段: 将当前执行上下文中的VO->AO, 此后,js引擎在当前环境从上到下、从左到右执行代码,不断改变AO中的变量等内容。当前上下文执行完毕则出栈,执行下一个上下文。

一样,咱们举一个例子进行分析:

var a = 1;
function bar() {
    var b = 2;

    function foo() {
        var c = 3;
    }

    foo();
    console.log(b)
}
function coo() {
    alert("hello")
}

bar()
coo()

咱们解释一下以上过程:

  1. 浏览器加载script标签;
  2. 语法分析;
  3. 预编译(全局),js进入全局函数环境,主线程建立全局执行上下文(Global EC),入栈;
  4. 全局执行,a=1;
  5. bar()调用,js进入bar函数环境,建立bar执行上下文(bar EC),入栈;
  6. bar执行,bar EC中VO(bar)->AO(bar),b=2;
  7. foo()调用,建立foo执行上下文(foo EC),入栈;
  8. foo执行,foo EC中VO(foo)->AO(foo),c=3;
  9. foo执行结束,出栈;
  10. 继续执行bar,console.log(b);
  11. bar执行结束,出栈;
  12. coo()调用;
  13. ...
  14. coo执行结束,出栈;
  15. 浏览器或者该标签页关闭,Global EC出栈;


至此,你已基本理解了js的执行机制。

参考文献:

js引擎的执行过程

深刻理解JavaScript系列(12):变量对象(Variable Object)

相关文章
相关标签/搜索