Execution Context(执行上下文)是 ECMA-262 标准中定义的一个抽象概念,用于同 Executable Code(可执行代码)进行区分。
标准中并没有从技术实现的角度定义 Execution Context 准确类型和结构,以各引擎的具体实现为准。
合法的,可以被解释器解析执行的代码。
Excutable Code 有这么几类:
eval()
函数执行的代码Execution Context 是 ES 用来跟踪代码运行状态和相关资源集合的特殊机制。它决定了 Executable Code 执行的过程中可以访问的数据。
依据 Excutable Code 的分类, Excution Context 也有相应的分类:
解释基本工作方式的过程中,会涉及到两个名词,先解释一下:
基本工作方式:Runnig Execution Context 总是在 Execution Context Stack 的顶部,Global Execution Context 总在 Execution Context Stack 的底部。无论什么时候,只要控制权从与当前 Running Execution Context 相关的可执行代码上切换到另一部分与当前 Running Execution Context 不相关的可执行代码上,一个新的 Execution Context 就会被创建,新创建的 Execution Context 会被放在当前的 Runnig Execution Context 的上面,成为新的 Running Execution Context。
╔═══════════════════════════════╗║ ┌─────────────────────────┐ ║║ │Running Execution Context│ ║║ └─────────────────────────┘ ║║ ┌─────────────────────────┐ ║║ │ Execution Context │ ║║ └─────────────────────────┘ ║║ ║║ ■ Execution ║║ ■ Context Stack ║║ ■ ║║ ║║ ┌─────────────────────────┐ ║║ │ Execution Context │ ║║ └─────────────────────────┘ ║║ ┌─────────────────────────┐ ║║ │ Execution Context │ ║║ └─────────────────────────┘ ║║ ┌─────────────────────────┐ ║║ │Global Execution Context │ ║║ └─────────────────────────┘ ║╚═══════════════════════════════╝ |
---|
如前言中提到的,ES 标准中并没有从技术实现的角度定义 Execution Context 准确类型和结构,为了更方便地解释 Excutable Code 和 Execution Context 之间的关系,暂且用数组表示 Execution Context Stack,然后用伪代码来操作 Execution Context Stack(省得画图了):
ECStack = [] |
---|
解释器在解析 Executable Code 时,为其创建对应的 Execution Context。
ECStack = [globalContext] |
---|
需要注意的是,函数代码中不包括内部函数的代码。比如:
(function foo(bar) {if (bar) {return}foo(true);})() |
---|
// 第一次调用 fooECStack = [<foo> functionContext,globalContext]// 第二次调用 fooECStack = [<foo> functionContext – recursively,<foo> functionContext,globalContext] |
---|
Eval Code 引入了一个新的概念 —— Calling Context(调用上下文),这是一个当 eval()
函数被调用时才会产生的 Context,并且 eval()
的活动(变量声明或函数声明)会影响到 Calling Context 。
// influence global contexteval('var x = 10')(function foo() {// and here, variable 'y' is created// in the locale context of 'foo' functioneval('var y = 20')})()console.log(x) // 10console.log(y) // 'y' is not defined |
---|
在严格模式中,
eval()
将在本地沙盒(local sandbox)中执行,不再影响Calling Context
。
上面的例子执行过程中,ECStack
的变化如下:
ECStack = [globalContext]// eval('var x = 10')ECStack.push({context: evalContext,callingContext: globalContext,})// eval exited contextECStack.pop()// foo function callECStack.push(<foo> functionContext)// eval('var y = 20')ECStack.push({context: evalContext,callingContext: <foo> functionContext})// return from evalECStack.pop()// return from fooECStack.pop() |
---|
this
属性。undefined
。arguments
对象。无法通过代码来直接访问 Variable Object,只有解析器才能访问它。 上面,关于 Scope Chain 的中文描述不怎么准确,来看这段英文:The scope chain property of each Execution Context is simply a collection of the current Execution Context’s Variable Object + all parent’s lexical Variable Object. (来源)
举一个例子来解释这个过程:
function foo(i) {var a = 'hello'var b = function privateB() {}function c() {}}foo(22) |
---|
调用 foo(22)
的时候,会在 Creation Stage 中创建这样的 Variable Object:
fooExecutionContext = {scopeChain: [ ... ],variableObject: {arguments: {0: 22,length: 1,},i: 22,c: pointer to function c(),a: undefined,b: undefined,},this: { ... }} |
---|
能看到,在 Creation Stage 中,虽然定义了属性名,但是并没有对它们初始化。Creation Stage 完成后,开始 Code Execution Stage:
fooExecutionContext = {scopeChain: [ ... ],variableObject: {arguments: {0: 22,length: 1,},i: 22,c: pointer to function c()a: 'hello',b: pointer to function privateB()},this: { ... }} |
---|
往期精选文章 |
---|
一小时内搭建一个全栈Web应用框架 |
全栈工程师技能大全 |
一个治愈JavaScript疲劳的学习计划 |
推翻JavaScript中的三座大山:作用域篇 |
掌握Chrome开发工具:新一代前端开发技术 |
WEB前端性能优化常见方法 |
在 Vue 中创建自定义输入 |
干货:CSS 专业技巧 |
四步实现React页面过渡动画效果 |
理解CSS模块化 |
小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。