js[执行上下文、执行栈]探究

众所周知,代码在执行的时候是会进入到对应的代码执行环境的,js代码在执行的时候会形成不同的执行上下文,而这些执行上下文一起就构成了执行栈(执行上下文栈)。

js中有三种代码运行环境(es5):

  1. 全局上下文(全局环境)
  2. 函数上下文(函数环境)
  3. eval运行环境(使用eval()方法运行代码所产生的环境) – 这个一般用的非常少
    参考:https://www.w3school.com.cn/jsref/jsref_eval.asp

示例

js代码的执行规律:js代码从上往下执行,首先进入的是全局上下文,然后将全局上下文中的函数提前(函数提升)并构建其函数上下文,当函数上下文构建完毕后,就执行函数里面的后续代码,当函数里面的代码执行完毕后在执行外面的代码,依此类推,从而形成执行栈。

	// 全局环境变量a
	var a = "全局环境变量a";
    // 全局环境运行outer - 先运行,后定义也不会报错,因为同一个上下文中函数将提前(函数提升)创建执行
    outer();
    // 打印a - 这里a的打印顺序会在outer执行以后
    console.log(a);
     // 全局函数func
    function func() {
    
    
      var d = 'func函数d';
      console.log(d);
    }
    // 全局函数outer
    function outer() {
    
    
      var b = "outer函数b";
      console.log(b);
      // outer函数中的inner
      function inner() {
    
    
        var c = "inner函数c";
        console.log(c);
        // 在inner中运行func
        func();
      }
      // 在outer中运行inner
      inner();
    }

运行结果:
在这里插入图片描述

解读:

运行开始

  1. 首先创建全局上下文,并且将全局上下文中的函数(outer和func)提升,及a变量提升(此时a为undefined),然后给a变量赋值,执行函数outer,此时进入outer函数。
  2. 进入outer函数后,创建outer函数上下文,然后将函数(inner)提升,及b变量提升(此时b为undefined),之后给b变量赋值,打印b变量,最后执行函数inner(),此时进入inner函数
  3. 进入inner函数后,创建inner函数上下文,由于没有函数定义,因此仅对c变量提升(此时c为undefined),然后给c变量赋值,打印c变量,最后执行func函数,此时进入func函数
  4. 进入func函数后,创建func函数上下文,对d变量提升(此时d为undefined),然后给d变量赋值,打印d变量,整个outer函数执行完毕,按照顺序最后执行打印a变量。

运行结束。

注意:函数提升和变量提升与执行上下文创建的两个阶段有关系。

执行栈的执行顺序如下:

在这里插入图片描述

执行上下文的三个重要属性(重点)

1.变量对象(Variable object,VO):变量对象一般包含变量、函数声明、函数的形参这三种内容
2.作用域链(Scope chain):保存这个上下文与其他上下文之间的作用域关系,这也是为什么里面的函数能够调用外面的函数的参数的原因。
3.this指针:

执行上下文创建过程(重点)

执行上下文创建过程有两个阶段:创建阶段及执行阶段
创建阶段(当函数被调用,但是开始执行函数内部代码之前)

  1. 创建并设置作用域链Scope chain
  2. 创建并设置变量对象VO/AO
  3. 设置this的指向

执行阶段
初始化变量对象,即变量赋值、函数引用,执行代码。

补充:
执行上下文可同时存在多个,但运行时执行上下文只有一个。

关于闭包

https://blog.csdn.net/lxy869718069/article/details/106802023

关于变量提升和函数提升

变量(函数)提升与执行上下文的创建过程有着紧密的联系。
变量(函数)提升:即函数及变量的声明都将被提升到上下文的最顶部,即变量或函数先使用后声明。

	// 先使用a变量
    console.log(a); // => undefined
    // 后声明a变量,并且赋值
    var a = 'a变量';
    // 打印赋值后的a
    console.log(a); // => a
    // 使用b函数
    b();  // => 打印b
    // 后声明b函数
    function b(){
    
    
      console.log('b');
    }

关于自执行函数

https://www.jianshu.com/p/ca50b6d0af9b

关于作用域

https://editor.csdn.net/md/?articleId=103868647

猜你喜欢

转载自blog.csdn.net/lxy869718069/article/details/106791617