JavaScript(七)執行上下文

JavaScript深入系列第七篇,結合之前所講的四篇文章,以權威指南的demo為例,具體講解當函數執行的時候,執行上下文棧、變量對象、作用域鏈是如何變化的。

前言

在《JavaScript深入之執行上下文棧》中講到,當 JavaScript 代碼執行一段可執行代碼(executable code)時,會創建對應的執行上下文(execution context)。

對於每個執行上下文,都有三個重要屬性:

  • 變量對象(Variable object,VO)
  • 作用域鏈(Scope chain)
  • this

然後分別在《JavaScript深入之變量對象》、《JavaScript深入之作用域鏈》、《JavaScript深入之從ECMAScript規範解讀this》中講解了這三個屬性。

閱讀本文前,如果對以上的概念不是很清楚,希望先閱讀這些文章。

因為,這一篇,我們會結合著所有內容,講講執行上下文的具體處理過程。

思考題

在《JavaScript深入之詞法作用域和動態作用域》中,提出這樣一道思考題:

<code>var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){

return scope;
}
return f();
}
checkscope();/<code>
<code>var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();/<code>

兩段代碼都會打印'local scope'。雖然兩段代碼執行的結果一樣,但是兩段代碼究竟有哪些不同呢?

緊接著就在下一篇《JavaScript深入之執行上下文棧》中,講到了兩者的區別在於執行上下文棧的變化不一樣,然而,如果是這樣籠統的回答,依然顯得不夠詳細,本篇就會詳細的解析執行上下文棧和執行上下文的具體變化過程。

具體執行分析

我們分析第一段代碼:

<code>var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();/<code>

執行過程如下:

1.執行全局代碼,創建全局執行上下文,全局上下文被壓入執行上下文棧

<code>    ECStack = [
globalContext
];/<code>

2.全局上下文初始化

<code>    globalContext = {
VO: [global, scope, checkscope],
Scope: [globalContext.VO],
this: globalContext.VO
}/<code>

2.初始化的同時,checkscope 函數被創建,保存作用域鏈到函數的內部屬性[[scope]]

<code>    checkscope.[[scope]] = [
globalContext.VO
];/<code>

3.執行 checkscope 函數,創建 checkscope 函數執行上下文,checkscope 函數執行上下文被壓入執行上下文棧

<code>    ECStack = [
checkscopeContext,
globalContext
];/<code>

4.checkscope 函數執行上下文初始化:

  1. 複製函數 [[scope]] 屬性創建作用域鏈,
  2. 用 arguments 創建活動對象,
  3. 初始化活動對象,即加入形參、函數聲明、變量聲明,
  4. 將活動對象壓入 checkscope 作用域鏈頂端。

同時 f 函數被創建,保存作用域鏈到 f 函數的內部屬性[[scope]]

<code>    checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope: undefined,
f: reference to function f(){}
},
Scope: [AO, globalContext.VO],
this: undefined
}/<code>

5.執行 f 函數,創建 f 函數執行上下文,f 函數執行上下文被壓入執行上下文棧

<code>    ECStack = [
fContext,
checkscopeContext,
globalContext
];/<code>

6.f 函數執行上下文初始化, 以下跟第 4 步相同:

  1. 複製函數 [[scope]] 屬性創建作用域鏈
  2. 用 arguments 創建活動對象
  3. 初始化活動對象,即加入形參、函數聲明、變量聲明
  4. 將活動對象壓入 f 作用域鏈頂端
<code>    fContext = {
AO: {
arguments: {
length: 0
}
},
Scope: [AO, checkscopeContext.AO, globalContext.VO],
this: undefined
}/<code>

7.f 函數執行,沿著作用域鏈查找 scope 值,返回 scope 值

8.f 函數執行完畢,f 函數上下文從執行上下文棧中彈出

<code>    ECStack = [
checkscopeContext,
globalContext
];/<code>

9.checkscope 函數執行完畢,checkscope 執行上下文從執行上下文棧中彈出

<code>    ECStack = [
globalContext
];/<code>

第二段代碼就留給大家去嘗試模擬它的執行過程。

<code>var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();/<code>

不過,在下一篇《JavaScript深入之閉包》中也會提及這段代碼的執行過程。

---------轉自冴羽


分享到:


相關文章: