詳解JavaScript中的執行上下文及呼叫堆疊
一、執行上下文是什麼
程式碼執行是在一定的環境之中執行的,這個執行環境我們就成為執行環境,也就是執行上下文,按照執行環境不同,我們可以分為三類:
全域性執行環境:程式碼首次執行時候的預設環境
函式執行環境:每當執行流程進入到一個函式體內部的時候
Eval執行環境:當eval函式內部的文字執行的時候
二、執行上下文棧是什麼
既然是‘棧',那就得符合‘棧'的特性,即資料結構是先進後出。下面我們看一段程式碼:
function cat(a){ if(a<0){ return false; } console.log('入棧:'+a); cat(a-1); console.log('出棧:'+a); } cat(3http://www.cppcns.com); // 入棧:3 // 入棧:2 // 入棧:1 // 入棧:0 // 出棧:0 // 出棧:1 // 出棧:程式設計客棧2 // 出棧:3
我們來分析上面的程式碼的執行過程:
①瀏覽器載入時,程式進入全域性執行上下文,將其壓入一個棧內(第一個進入的,所以最底層);該執行上下文下只有一個函式cat、cat呼叫、引數3;
②程式進入cat函式內,即進入該函式執行上下文,也將其壓入棧內(第二個進入的,所以倒數第二層),因為引數為3大於0,繼續往下執行,輸出'入棧:3';
③程式繼續執行,呼叫函式cat,再次進入新的函式執行上下文,繼續壓入棧內(第三個進入,倒數第三層),引數為a-1,迴圈②步驟;這裡,需要注意的是,因為呼叫了函式cat(a-1),導致下一行程式碼'出棧:a'(此時a仍為3),也就是'出棧:3'暫時擱淺起來,存在棧內倒數第二層
④不斷重複②③步驟,以次輸出'入棧:2'、'入棧:1'、'入棧:0';同時被擱淺的有'出棧:2'(棧內倒數第三層)、'出棧:1(棧內倒數第四層)'、'出棧:0(棧內倒數第五層)';程式設計客棧
⑤按照棧的特性,被擱淺起來的4個輸出項:以次輸出'入棧:3'、'入棧:2'、'入棧:1'、'入棧:0';
以上就是執行上下文棧的具體情況,請大家手動程式碼練習一下,相信會容易理解透徹。
三、執行上下文棧的過程細節
我們已經知道,每次呼叫函式都會執行一個新的上下文www.cppcns.com,每個執行上下文都分為兩個階段:建立階段、執行階段
建立階段:指的是程式呼叫函式,但程式碼未執行時的階段;
執行階段:指的是變數分配、函式執行等程式碼執行階段;
(一)建立階段
該階段會呼叫函式數時,建立一個執行上下文物件(executionContextObj),該物件又包含了三個物件,分別是作用域鏈物件(scopeChain)、變數物件(variableObject,簡稱VO)、this物件,其中VO包括變數宣告(variable)、函式宣告(function)、引數(arguments)等。
這三個物件分別是在三個步驟建立的:
步驟1:初始化作用域鏈(scopeChain),開闢棧記憶體
步驟2:建立引數、函式、變數
步驟3:決定上下文的'this'的值
結合程式碼來進一步分析一下上面的步驟:
function cat(name) { var a = '年年'; var b = function () {}; this.name = name; function c() { console.log(this.name); } c(); } cat('有魚');
這段程式碼在呼叫函式 cat('有魚')時,執行上下文是處於 建立階段的,程式碼解析為:
cat執行上下文物件 = { scopeChain: { ... },// 1.建立作用域鏈,開闢棧記憶體 variableObject: { 2.建立變數物件 arguments: { // 2.1 解析引數 0: '有魚',length: 1 },name: '有魚',// 2.1 解析引數,建立形參名稱,並進行引數賦值 c: function c() // 2.2 找到函式宣告c,並將c作為屬性,function c作為值 a: undefined,// 2.3 找到變數宣告a,初始化為undefined,該階段只看宣告部分,不進行賦值 b: undefined // 2.3 找到變數宣告b,初始化為undefined,該階段只看宣告部分,不進行賦值 },this: { 3.建立this物件 this:cat('有魚') // 3.1 指向此次呼叫的物件 name:undefined // 3.2 物件屬性name的初始化為undefined }; c() //又進入函式c執行上下文,跟cat函式一樣,暫不展開 }
通過程式碼解析我們可以得出以下結論
①三個步驟順序不能亂
②VO步驟內,先執行函式宣告,再執行變數宣告
③只有引數可以在此階段進行賦值,變數、函式都只能宣告
(二)執行階段
該階段js直譯器執行上下文中的函式程式碼,逐行執行js程式碼,並給變數賦值;
還是結合程式碼來分析:
c程式設計客棧at執行上下文物件 = {
scopeChain: { ... },variableObject: {
arguments: {
0: '有魚',c: function c(),a: '年年',// 變數a進行賦值
b: function b // 變數b進行賦值
},this: { 3.建立this物件
this:cat('有魚')
name:'有魚' // 物件屬性name進行賦值
}
}
以上就是詳解javascript中的執行上下文及呼叫堆疊的詳細內容,更多關於javaScript中的執行上下文及呼叫堆疊的資料請關注我們其它相關文章!