JS基础篇:6、JS执行上下文与预编译(预解析)

1,694 阅读4分钟

背景或定义

执行上下文, 其实就是js代码运行时所处的环境。

js中的变量有全局变量和局部变量之分, 所以执行上下文也有区分。

  • 全局变量: 定义在 全局环境
  • 局部变量: 定义在 函数内部

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

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

预编译(预解析)

在上下文创建以后, 并不会立即执行JS代码, 而是会先进行一个预编译的过程, 根据上下文的不同, 预编译又可以分为:

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

全局预编译解析流程

var 还有 function 提升到当前作用域的最前面

  • 遇到变量声明, 变量名作为VO对象的属性名, 属性值置为 undefined

  • 遇到函数声明, 函数名作为VO对象的属性名, 属性值为函数本身

  • 如果函数名与变量名冲突(相同), 函数声明会将变量声明覆盖, 属性值就是函数本身

  • 预编译结束以后, 再逐行执行代码

console.log(fn); // undefined
var fn = 1;
console.log(fn) // 1

上述代码执行:遇到变量声明,变量名作为VO对象的属性名,值为undefined; vo对象就是 {fn : undefined};无其他声明从上到下执行代码第一个log: undefined, 变量赋值 第二个log: 1;

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

上述代码执行:遇到变量声明,变量名作为VO对象的属性名,值为undefined;遇到函数声明且和变量名冲突,函数本身作为属性值且覆盖变量声明属性值;VO对象{fn : fucntion () {}};无其他声明从上到下执行代码第一个log: function,变量赋值此时修改vo对象{fn : 1},再次遇到函数声明跳过,第二个log: 1。

console.log(fn); // undefined
var fn = 1;
var fn = function  () {};
console.log(fn) // function

上述代码执行:要注意这里我们声明函数使用var所以他是变量声明,因此第一个log是undefined,第二个log时fn赋值是function所以打印funcition;

函数预编译

上面我们提到过函数执行上下文是在函数被调用的时候创建的

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

而函数预编译与全局预编译, 唯一的不同之处就在于: 函数可能会有形参

还有一点区别就是全局的VO(变量对象)在函数预编译阶段被叫做AO (活动对象), 两者本质一样, 只不过换了个说法。

函数预编译解析流程

  • 遇到变量声明, 变量名作为AO对象的属性名, 属性值置为 undefined
  • 遇到形参, 形参名作为AO对象的属性名, 属性值置为 undefined
  • 如果形参名与变量名冲突, 形参会将变量声明覆盖
  • 将实参的值赋予形参, 即替换 AO对象中形参的属性值
  • 遇到函数声明, 函数名作为AO对象的属性名, 属性值为函数本身
  • 如果函数名与变量名冲突, 函数声明会将变量声明覆盖

通俗的说就是变量声明AO{key :undefined},如果有实参和变量名相同AO对象{key : 实参值},如果有函数名与实参名相同AO对象{key : function}。归纳:函数声明优先级 > 实参 > 变量声明

function fn (a,b) {
  console.log(a); // 3 实参值
  var a = 1;
  console.log(a); // 1
  console.log(b); // undefined
}
fn(3);

上述代码执行:按照预解析流程我们可以很清楚的知道打印结果

function fn (a,b) {
  console.log(a); // function
  var a = 1;
  function a () {}
  console.log(a); // 1
}
 fn(3);

上述代码执行:按照流程变量声明:AO{a : undefined} ,有实参AO{a: 3},函数声明AO{a: function(){console.log(2)}};所以第一个log是 function 第二个log打印有赋值所以是1

总结

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