javascript代码是怎么执行的?

var val = 1;

function foo() {
    console.log(val);
}

foo();
// 控制台打印1

上段代码测试下来似乎javascript是顺序执行的,再看一段代码:

var val = 1;

function foo() {
    console.log(val);
}

foo();

function foo() {
    console.log(val + 'next');
}

// 最终控制台打印‘1next’

按照之前的猜测,代码执行是这样的:

声明变量val并赋值1;

  声明函数foo;执行函数foo;

  声明同名函数foo。

但结果却不是这样的,js引擎在解释执行js代码的时候并不是一行一行顺序执行的,而是一段一段解释执行的。那在执行一段代码之前是需要进行一些准备工作的,这个准备工作严谨点来说就称为执行上下文

 

执行上下文栈

我们前面所说的一段一段代码是怎么划分的呢?代码段包含全局代码函数代码eval代码,这些都称为可执行代码

  通常我们的代码中会包含很多代码段,那引擎是怎么管理这么多代码段执行的呢?

  其实js引擎会创建一个执行上下文栈来管理这么多执行上下文(我们都知道栈遵循后进先出)

  通过一段代码来分析一下引擎是如何管理执行上下文的

function foo() {
    console.log('output');
}

function bar() {
    foo();
}

function baz() {
    bar();
}

baz();

1、// baz()

    创建baz执行上下文,并推入栈中

  2、// baz函数中调用了bar函数

    创建bar函数的执行上下文,并推入栈中

  3、// bar函数中调用了foo函数

    创建foo函数的执行上下文,并推入栈中

  4、// foo() 执行完毕

    将foo的执行上下文弹出栈外

  5、// bar() 执行完毕

    将bar的执行上下文弹出栈外

  6、// baz() 执行完毕

    将baz的执行上下文弹出栈外

  (其实还少了一个:全局的执行上下文栈)

  函数的执行上下文前面已经介绍了,是在函数执行之前所做的一些准备工作,那执行上下文到底又是什么呢?

  个人觉得简单点理解可以这样认为: 执行上下文是包含这几个属性的: 变量对象、作用域链、this;

 

下面就分别看一下变量对象、作用域链是什么;

变量对象

  变量对象其实就是函数执行时和函数执行上下文相关的作用域

  变量对象包括下面的属性:

    函数的形式参数

  • 如果没有实际参数,则值为undefined

    函数内部的函数声明

  • 如果变量对象中已经包含同名属性,则函数声明会完全覆盖该属性

    函数内部的变量声明

  • 如果变量对象中已经包含同名属性,则变量声明会被忽略

  结合代码:

var glo = 2;
function foo(val) {
    var temp = 1;
    function bar() {
        return glo + temp + val;
    }
    return bar();
}
foo(3);

函数foo执行时的变量对象就是这样的:

  AO = 

    {
      arguments: {val: 3}, // 形式参数
      bar: function, // 函数声明
      temp: undefined, // 变量声明
    }

  接下来执行代码,会使用或者修改变量对象的值

作用域链

  通俗得说作用域链就是当前环境能访问到的变量的集合组成的链条

  

var glo = 1;
function foo(val) {
  var local = 2; 
  return val + local + glo;
}

 

函数foo在创建的时候就确定了其内部可访问的作用域范围: 函数作用域中包含local、形式参数val、上层作用域(全局作用域)中glo