深入理解 js 执行上下文

120 阅读3分钟

什么是执行上下文

执行上下文 就是js 的执行环境

执行环境做什么用?

执行环境是在运行js 语句前进行预编译,编译好的环境,分为全局环境和局部环境。全局环境里的变量叫做全局变量,局部环境内的变量叫做局部变量。

通常函数体内部的变量就是局部变量。

对于全局环境, 我们又称之为 全局上下文 全局上下文是打开浏览器就自动创建的,在浏览器中,全局上下文就是我们常说的 window 对象。

对于函数内部环境, 称之为 函数上下文 函数上下文是在函数被调用的时候创建的(记住执行调用的时候)

我们了解了环境和变量,那我们看看js 执行代码的时候都干了什么?

  1. 语法分析 就是检查缩写的代码是不是合格的。
  2. 预编译 执行代码前,将左右的代码编译成可逐行的代码
  3. 解释执行 一行一行的执行

预编译

每个执行上下文都有一个与之相关联的变量对象 (Variable Object, 简称 VO, 初其实就是一个对象:{key : value}形式) , 当前执行上下文中所有的变量函数都添加在其中。

全局预编译

大概的简述: 代码在执行前会将变量和函数提前声明,放在代码块的最顶端,再进行赋值操作。

全局执行预编译的流程

变量和函数提升到最前面

  • 将变量名先定义到VO 中,变量的名作为key,值为undefined
  • 再将函数提升到到VO 中,函数的名作为key,值为函数体;
  • 如果函数名称和变量名称相同,则覆盖变量,属性值就是函数本身
  • 预编译结束以后, 再逐行执行代码
    var a = 3;   
    function a(){
    };
    console.log(a);

结果 3

console.log(a); // [Function: a] 
var a = 1; 
function a () {}; 
console.log(a) // 1

上述代码:如果函数名称和变量名称相同,则覆盖变量,属性值就是函数本身

局部预编译

函数执行上下文是在函数被执行的时候创建的

只要函数不执行, 那么就不会进行函数的预编译

在函数预编译阶段被叫做AO (活动对象)

函数预编译解析流程

  • 遇到变量声明, 变量名作为AO对象的属性名, 属性值置为 undefined
  • 遇到形参, 形参名作为AO对象的属性名, 属性值置为 undefined
  • 如果形参名与变量名冲突, 形参会将变量声明覆盖
  • 将实参的值赋予形参, 即替换 AO对象中形参的属性值
  • 遇到函数声明, 函数名作为AO对象的属性名, 属性值为函数本身
  • 如果函数名与变量名冲突, 函数声明会将变量声明覆盖
 function b(){
      console.log(window)
      console.log(b)   // f b()
      b = 6
      console.log(b)  // 6
    }
    b()
    //函数的调用之前 全局执行上下文window中产生了b函数体,等待执行 
    // b 函数的this 指向的  window  所以第一个结果值 是 window 里的 function b()
    // 函数体内的b  没有var声明就是全局变量,赋值给 b = 6 就等于给全局的var  b = function() {} 赋值,所以 第二次b 的结果是 6


 setTimeout(function a(){
      debugger;
      console.log(a)   //定时器内部的函数自动执行 ,执行前形成上下文,变量 a 没有var ,就提升变量到外部作用域, 再声明函数a,当函数a 和变量同名,则覆盖变量a,所以a是函数
      a= 8            //变量 a 没有var ,就提升变量到外部作用域  已经被覆盖了
      console.log(a)  //所以这里的a  也是函数a

    },1000)

归纳:函数声明优先级 > 实参 > 变量声明

在上下文执行预解析中,变量声明的优先级是最低的,函数声明的优先级是最高的,函数实参优先级是居于他俩之间的。