更新:謝謝你們的支持,最近折騰了一個博客官網出來,方便你們系統閱讀,後續會有更多內容和更多優化,猛戳這裏查看前端
------ 如下是正文 ------node
本期的主題是調用堆棧,本計劃一共28期,每期重點攻克一個面試重難點,若是你還不瞭解本進階計劃,文末點擊查看所有文章。webpack
若是以爲本系列不錯,歡迎點贊、評論、轉發,您的支持就是我堅持的最大動力。git
執行上下文是當前 JavaScript 代碼被解析和執行時所在環境的抽象概念。github
執行上下文總共有三種類型web
全局執行上下文:只有一個,瀏覽器中的全局對象就是 window 對象,this
指向這個全局對象。面試
函數執行上下文:存在無數個,只有在函數被調用的時候纔會被建立,每次調用函數都會建立一個新的執行上下文。算法
Eval 函數執行上下文: 指的是運行在 eval
函數中的代碼,不多用並且不建議使用。跨域
執行棧,也叫調用棧,具備 LIFO(後進先出)結構,用於存儲在代碼執行期間建立的全部執行上下文。數組
首次運行JS代碼時,會建立一個全局執行上下文並Push到當前的執行棧中。每當發生函數調用,引擎都會爲該函數建立一個新的函數執行上下文並Push到當前執行棧的棧頂。
根據執行棧LIFO規則,當棧頂函數運行完成後,其對應的函數執行上下文將會從執行棧中Pop出,上下文控制權將移到當前執行棧的下一個執行上下文。
var a = 'Hello World!';
function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
function second() {
console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');
// Inside first function
// Inside second function
// Again inside first function
// Inside Global Execution Context
複製代碼
執行上下文分兩個階段建立:1)建立階段; 2)執行階段
一、肯定 this 的值,也被稱爲 This Binding。
二、LexicalEnvironment(詞法環境) 組件被建立。
三、VariableEnvironment(變量環境) 組件被建立。
直接看僞代碼可能更加直觀
ExecutionContext = {
ThisBinding = <this value>, // 肯定this LexicalEnvironment = { ... }, // 詞法環境 VariableEnvironment = { ... }, // 變量環境 } 複製代碼
全局執行上下文中,this
的值指向全局對象,在瀏覽器中this
的值指向 window
對象,而在nodejs
中指向這個文件的module
對象。
函數執行上下文中,this
的值取決於函數的調用方式。具體有:默認綁定、隱式綁定、顯式綁定(硬綁定)、new
綁定、箭頭函數,具體內容會在【this全面解析】部分詳解。
詞法環境有兩個組成部分
一、環境記錄:存儲變量和函數聲明的實際位置
二、對外部環境的引用:能夠訪問其外部詞法環境
詞法環境有兩種類型
一、全局環境:是一個沒有外部環境的詞法環境,其外部環境引用爲 null。擁有一個全局對象(window 對象)及其關聯的方法和屬性(例如數組方法)以及任何用戶自定義的全局變量,this
的值指向這個全局對象。
二、函數環境:用戶在函數中定義的變量被存儲在環境記錄中,包含了arguments
對象。對外部環境的引用能夠是全局環境,也能夠是包含內部函數的外部函數環境。
直接看僞代碼可能更加直觀
GlobalExectionContext = { // 全局執行上下文
LexicalEnvironment: { // 詞法環境
EnvironmentRecord: { // 環境記錄
Type: "Object", // 全局環境
// 標識符綁定在這裏
outer: <null> // 對外部環境的引用
}
}
FunctionExectionContext = { // 函數執行上下文
LexicalEnvironment: { // 詞法環境
EnvironmentRecord: { // 環境記錄
Type: "Declarative", // 函數環境
// 標識符綁定在這裏 // 對外部環境的引用
outer: <Global or outer function environment reference>
}
}
複製代碼
變量環境也是一個詞法環境,所以它具備上面定義的詞法環境的全部屬性。
在 ES6 中,詞法 環境和 變量 環境的區別在於前者用於存儲**函數聲明和變量( let
和 const
)綁定,然後者僅用於存儲變量( var
)**綁定。
使用例子進行介紹
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
複製代碼
執行上下文以下所示
GlobalExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// 標識符綁定在這裏
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >
}
outer: <null>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// 標識符綁定在這裏
c: undefined,
}
outer: <null>
}
}
FunctionExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 標識符綁定在這裏
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GlobalLexicalEnvironment>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 標識符綁定在這裏
g: undefined
},
outer: <GlobalLexicalEnvironment>
}
}
複製代碼
變量提高的緣由:在建立階段,函數聲明存儲在環境中,而變量會被設置爲 undefined
(在 var
的狀況下)或保持未初始化(在 let
和 const
的狀況下)。因此這就是爲何能夠在聲明以前訪問 var
定義的變量(儘管是 undefined
),但若是在聲明以前訪問 let
和 const
定義的變量就會提示引用錯誤的緣由。這就是所謂的變量提高。
此階段,完成對全部變量的分配,最後執行代碼。
若是 Javascript 引擎在源代碼中聲明的實際位置找不到 let
變量的值,那麼將爲其分配 undefined
值。
進階系列文章彙總:github.com/yygmind/blo…,內有優質前端資料,歡迎領取,以爲不錯點個star。
我是木易楊,網易高級前端工程師,跟着我每週重點攻克一個前端面試重難點。接下來讓我帶你走進高級前端的世界,在進階的路上,共勉!