窥探javaScript创建执行上下文

1,003 阅读5分钟

后文可获取js创建上下文脑图

参考文章 http://suo.im/682mqK

1 什么是执行上下文?

执行上下文就是一个JavaScript代码评估和执行的抽象环境

2 执行上下文有几种类型?

在JavaScript中有三种类型的执行上下文:

  • 全局执行上下文---在浏览器中由全局对象windowc创建, this被绑定为全局对象,一个程序中只有一个全局上下文

  • 函数执行上下文--- 函数被调用的时候会这个函数创建一 个执行上下文

  • eval执行上下文--- 在eval中执行的code也有自己的上下文

3 js是如何创建执行上下文的?

创建执行上下文主要分为两个阶段 创建阶段(js引擎会扫描并编译function内的代码,并不会执行任何代码) & 执行阶段(JS引擎会再次扫描函数,用变量的值更新变量对象,并执行代码)

3.1 创建阶段

创建词法环境

什么是词法环境呢?
    简单来说词法环境就是标识符与变量的映射关系,标识符通常为变量或方法的名称,而变量通常是对真实对象的引用,如下例子

var a = 20;
var b = 40;
function foo() {
 console.log('c');
}
// 对应的词法环境为:
lexicalEnvironment = {
 a: 20,
 b: 40,
 foo: <ref. to foo function>
}

词法环境的结构是怎样的呢?
    由上面例子,你可能已经猜出个大概,词法环境由三部分组成

  • this绑定
        在这个组件中this的值被确定,或者被设置在全局上下文中this指代全局对象(浏览器中指代window)在函数上下文中,this指代的对象依赖于函数被引用或被调用是方式
  • 环境记录器
        环境记录器存储了当前词法环境中变量和函数的生命位置,有如下两种类型
    • 声明式环境记录器(存储变量(包含arguments)和函数的声明,函数的词法环境包含了声明式环境记录器)
    • 目标环境记录器(全局代码的词法环境包含了目标环境记录器,目标环境记录器除了存储了变量和函数的声明,还记录了全局绑定对象(浏览器中为window),全局绑定对象的每个属性和方法都会被创建为一条条的记录)
  • 外部引用
        它意味着当前词法环境有权访问外部词法环境,即js引擎在当前词法环境中找不到变量时,它会到外部的词法环境中寻找

创建变量环境

什么是变量环境呢?
    变量环境也是词法环境,它拥有词法环境所有的属性和组件的定义在ES6中,词法环境和变量环境唯一不同,在于词法环境存储了函数和let, const变量的绑定,而变量环境只用来存储var类型变量的绑定[后文附举例代码]

3.2 执行阶段

在这个阶段,所有变量的赋值都已完成,代码开始执行

4 具体创建过程

用如下全局代码演示
1.有如下全局代码

let a = 20;
const b = 30
var c;
function multiply(e, f) {
 var g = 20;
 return e * f * g;
}
c = multipley(2030);

2.全局上下文创建阶段,创建全局上下文
    可以看到在此阶段,a,b均为初始化,c已初始化,但未赋值

GlobalExectionContext = {
	LexicalEnvironment: {
		EnvironmentRecord: {
			Type: "Object",
			a: <uninitiallized>, // 创建阶段的a未初始化
			b: <uninitiallized>, // 创建阶段的b未初始化
			mutiply: <func>
		}
		outer: <null>,
		ThisBinding: <Global Object>
	},
	VariableEnvironment: {
		EnvironmentRecord: {
			Type: "Object",
			c: undefined, // 创建阶段c已初始化,但未赋值
		}
		outer: <null>,
		ThisBinding: <Global Object>
	}
}

3.全局上下文执行阶段,变量被赋值
    可以看到在此阶段,a已赋值为20,b已赋值为30,c仍然未赋值

GlobalExectionContext = {
	LexicalEnvironment: {
		EnvironmentRecord: {
			Type: "Object",
			a:  20, // 变量已赋值
			b: 30,  // 变量已赋值
			mutiply: <func>
		}
		outer: <null>,
		ThisBinding: <Global Object>
	},
	VariableEnvironment: {
		EnvironmentRecord: {
			Type: "Object",
			c: undefined,
		}
		outer: <null>,
		ThisBinding: <Global Object>
	}
}

4.全局上下文执行遇到mutiply(20,30)函数,创建函数执行上下文,此时为函数上下文创建阶段
    可以看到函数上下文创建阶段g已声明,默认值为undefined
注意!普通函数的执行词法环境中包含arguments变量对象映射

FunctionExectionContext = {
	LexicalEnvironment: {
		EnvironmentRecord: {
			Type: "Declarative",
			Arguments: {0: 20, 1: 30, length: 2},
		}
		outer: <GlobalLexicalEnvironment>,
		ThisBinding: <Global Object or undefine>
	},
	VariableEnvironment: {
		EnvironmentRecord: {
			Type: "Declarative",
			g: undefined,
		}
		outer: <GlobalLexicalEnvironment>,
		ThisBinding: <Global Object or undefine>
	}
}

5. 函数上下文执行阶段,变量g被赋值
    执行阶段,此时g值为20

FunctionExectionContext = {
	LexicalEnvironment: {
		EnvironmentRecord: {
			Type: "Declarative",
			Arguments: {0: 20, 1: 30, length: 2},
		}
		outer: <GlobalLexicalEnvironment>,
		ThisBinding: <Global Object or undefine>
	},
	VariableEnvironment: {
		EnvironmentRecord: {
			Type: "Declarative",
			g: 20,
		}
		outer: <GlobalLexicalEnvironment>,
		ThisBinding: <Global Object or undefine>
	}
}

6. 函数执行完毕,返回c值,全局上下文更新,程序运行结束
    到此js创建及执行上下文栈的整个流程就结束了。
    整个创建执行过程,附有脑图(更容易理解),这里不好展示。感兴趣的朋友加我微信或评论区留言。