Open muwoo opened 6 years ago
在运行一段 js 代码的时候,就会生成一个可执行的上下文,根据不同类型的区分,可能会存在以下几种类型的代码执行上下文:
我们来看一段代码:
这段代码中,我们存在一个 global context 和 3 个不同的 function context。你可以随意创建这样的function context但是每一个函数都会创建一个新的执行上下文(EC)。函数外部定义的变量可以被函数内部使用,但是函数内部定义的变量确不能被函数外部使用,这是为什么呢?
global context
function context
浏览器解释器执行 js 是单线程的过程,这就意味着同一时间,只能有一个事情在进行。其他的活动和事件只能排队等候,生成出一个等候队列执行栈(Execution Stack):
我们知道,在一开始执行代码的时候,变确定了一个全局执行上下文global execution context作为默认值。如果在你的全局环境中,调用了其他的函数,程序将会再创建一个新的 EC,然后将此 EC推入进执行栈中execution stack
global execution context
execution stack
如果函数内再调用其他函数,相同的步骤将会再次发生:创建一个新的EC -> 把EC推入执行栈。一旦一个EC执行完成,变回从执行栈中推出(pop):
(function foo(i) { if (i === 3) { return; } else { foo(++i); } }(0));
上面这个函数完成了对自身的3次调用,第一次调用时,生成一个EC推入执行栈,在执行的过程中,又遇到一个新的函数foo(1)此时,又会创建一个新的EC,再次推入...
foo(1)
关于执行栈,有5个关键点可以了解一下:
从上文了解到了一旦函数被调用,都会创建一个新的执行上下文。但是在 js 解释器中,调用执行上下文有2个阶段:
创建阶段(函数被调用,但是还没有执行):
scope chain
激活/代码执行阶段
当每一个 function 被调用时,我们可以大致描述一下会创建这样一个executionContextObj对象
executionContextObj
executionContextObj = { 'scopeChain': { /* 变量对象 + 父执行上下文上的变量对象 */ }, 'variableObject': { /* function arguments / parameters,内部的 variable 和 function 声明 */ }, 'this': {} }
executionContextObj在函数被调用的时候创建,在创建阶段,会扫描函数的所有参数和变量。扫描的结果成了executionContextObj内部的variableObject属性。下面是执行步骤的细化:
variableObject
可以通过一段代码来分析一下:
function foo(i) { var a = 'hello'; var b = function privateB() { }; function c() { } } foo(22);
当我们执行foo(22)的时候,EC创建阶段会类似生成下面这样的对象:
foo(22)
fooExecutionContext = { scopeChain: { ... }, variableObject: { arguments: { 0: 22, length: 1 }, i: 22, c: pointer to function c() a: undefined, b: undefined }, this: { ... } }
正如你看到的,在创建阶段,会发生属性名称的定义,但是并没有赋值。一旦创建阶段(creation stage)结束,变进入了激活 / 执行阶段,那么fooExecutionContext便会完成赋值,变成这样:
fooExecutionContext
fooExecutionContext = { scopeChain: { ... }, variableObject: { arguments: { 0: 22, length: 1 }, i: 22, c: pointer to function c() a: 'hello', b: pointer to function privateB() }, this: { ... } }
执行上下文(Execution Context)
在运行一段 js 代码的时候,就会生成一个可执行的上下文,根据不同类型的区分,可能会存在以下几种类型的代码执行上下文:
我们来看一段代码:
这段代码中,我们存在一个
global context
和 3 个不同的function context
。你可以随意创建这样的function context
但是每一个函数都会创建一个新的执行上下文(EC)。函数外部定义的变量可以被函数内部使用,但是函数内部定义的变量确不能被函数外部使用,这是为什么呢?执行栈(Execution Context Stack)
浏览器解释器执行 js 是单线程的过程,这就意味着同一时间,只能有一个事情在进行。其他的活动和事件只能排队等候,生成出一个等候队列执行栈(Execution Stack):
我们知道,在一开始执行代码的时候,变确定了一个全局执行上下文
global execution context
作为默认值。如果在你的全局环境中,调用了其他的函数,程序将会再创建一个新的 EC,然后将此 EC推入进执行栈中execution stack
如果函数内再调用其他函数,相同的步骤将会再次发生:创建一个新的EC -> 把EC推入执行栈。一旦一个EC执行完成,变回从执行栈中推出(pop):
上面这个函数完成了对自身的3次调用,第一次调用时,生成一个EC推入执行栈,在执行的过程中,又遇到一个新的函数
foo(1)
此时,又会创建一个新的EC,再次推入...关于执行栈,有5个关键点可以了解一下:
执行上下文(EC)的一些细节
从上文了解到了一旦函数被调用,都会创建一个新的执行上下文。但是在 js 解释器中,调用执行上下文有2个阶段:
创建阶段(函数被调用,但是还没有执行):
scope chain
激活/代码执行阶段
当每一个 function 被调用时,我们可以大致描述一下会创建这样一个
executionContextObj
对象活动对象和变量对象(Activation / Variable Object [AO/VO])
executionContextObj
在函数被调用的时候创建,在创建阶段,会扫描函数的所有参数和变量。扫描的结果成了executionContextObj
内部的variableObject
属性。下面是执行步骤的细化:variableObject
:内部包含 arguments 对象;内部定义的函数,以及绑定上对应的变量环境;内部定义的变量可以通过一段代码来分析一下:
当我们执行
foo(22)
的时候,EC创建阶段会类似生成下面这样的对象:正如你看到的,在创建阶段,会发生属性名称的定义,但是并没有赋值。一旦创建阶段(creation stage)结束,变进入了激活 / 执行阶段,那么
fooExecutionContext
便会完成赋值,变成这样: