在《深刻學習js之——執行上下文棧》中說過,當JavaScript代碼執行一段可執行代碼(executable code)時,會建立對應的執行上下文(execution context)函數
對於每個執行上下文,都有三個重要的屬性:post
變量對象(Variable object VO)
做用域鏈(Scope chain)
this
本文咱們結合着這三個部分的內容,講講執行上下文的具體處理過程。學習
在《深刻學習js之——詞法做用域和動態做用域》中,提出這樣一道思考題:this
// 思考題一: var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f(); } checkscope(); // 思考題二: var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } checkscope()();
兩段代碼都會打印local scope
,可是仍是有些許差別的,本文就詳細的解析執行上下文棧和執行上下文的具體變化過程。code
咱們分析第一段代碼:對象
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f(); } checkscope();
執行過程以下:ip
一、執行全局代碼,建立全局執行上下文,全局上下文被壓入執行上下文棧作用域
ECStack = [ globalContext ];
二、全局上下文初始化get
globalContext = { VO: [global], Scope: [globalContext.VO], this: globalContext.VO }
二、初始化的同時,checkscope 函數被建立,保存做用域鏈到函數內部的屬性[[scope]]
io
checkscope.[[scope]] = [ globalContext.VO ];
三、執行checkScope 函數,建立checkScope 函數執行上下文,checkScope 函數執行上下文被壓入執行上下文棧:
ECStack = [ checkscopeContext, globalContext ];
四、checkscope 函數執行上下文初始化:
1.複製函數 [[scope]] 屬性建立做用域鏈,
2.用 arguments 建立活動對象,
3.初始化活動對象,即加入形參、函數聲明、變量聲明,
4.將活動對象壓入 checkscope 做用域鏈頂端,
同時 f 函數被建立,保存做用域鏈到 f 函數的內部屬性[[scope]]
checkscopeContext = { AO: { arguments: { length: 0 }, scope: undefined, f: reference to function f(){} }, Scope: [AO, globalContext.VO], this: undefined }
五、執行f函數,建立 f 函數執行上下文,f 函數執行上下文被壓入執行上下文棧
ECStack = [ fContext, checkscopeContext, globalContext ]
六、f 函數執行上下文初始化, 如下跟第 4 步相同:
1.複製函數[[scope]]
屬性建立做用域鏈
2.用 arguments 建立活動對象
3.初始化活動對象,即加入形參、函數聲明、變量聲明
4.將活動對象壓入 f 做用域鏈頂端
fContext = { AO: { arguments: { length: 0 } }, Scope: [AO, checkscopeContext.AO, globalContext.VO], this: undefined }
七、f 函數執行,沿着做用域鏈查找 scope 值,返回 scope 值
八、f 函數執行完畢,f 函數上下文從執行上下文棧中彈出
ECStack = [ checkscopeContext, globalContext ]
九、checkscope 函數執行完畢,checkscope 執行上下文從執行上下文棧中彈出
ECStack = [ globalContext ]