execution context,是 Javascript 代碼被解析和執行所在環境的抽像概念。做爲一個前端工程師,除了碼代碼,還得要知道代碼是如何運行的,好比 Javascript 引擎和執行上下文以及調出堆棧之間是怎麼工做的;javascript
能夠這麼理解,不在函數中的代碼都在全局執行上下文中。在此期間,共發生兩件事:前端
this
指針指向這個對象;注:一個程序中只有一個全局執行上下文對象,否則就亂套了。java
在函數調用時建立。想一想一個程序能夠有多少個函數,沒人定義,因此函數上下文是沒個數限制的。node
運行 Eval 函數中的代碼,因爲此函數不被 Javascript 開發人員待見,因此接下來的內容就不帶它玩了。數組
總結下,不在函數裏的就是全局上下文,其餘都是函數執行上下文(由於咱們不帶 Eval 函數執行上下文)。瀏覽器
棧,具備後進先出結構(LIFO, Last In First Out),具備 push 和 pop 功能。代碼執行期間所建立的執行上下文就是它存儲的。這麼一說,它的做用就提現出來了。那存儲執行上下文就是執行上下文棧咯。前端工程師
問題來了,全局執行上下文和函數執行上下文是何時進入執行棧中的呢?app
接下來,Javascript 引擎會執行棧頂端的函數,函數執行完成後就會從棧中彈出,而後執行下一個。函數
// context-stack-01.js
let name = 'pr';
const firstFunction = () => {
console.log('first function');
secondFunction();
console.log('first function again');
}
const secondFunction = () => {
console.log('second function');
}
firstFunction();
console.log(name);
// first function
// second function
// first function again
// pr
複製代碼
從代碼和流程圖可見post
firstFunction
函數調用時(此時函數內部代碼還未執行),引擎建立一個新的函數執行上下文,而後入棧;secondFunction
函數調用時(此時函數內部代碼還未執行),引擎建立一個新的函數執行上下文,而後入棧;secondFunction
函數執行完後,函數執行上下文出棧(銷燬);firstFunction
函數執行完後,函數執行上下文出棧(銷燬);爲了便於理解,用數組模擬下執行上下文棧的行爲
// ESC = execution context stack
ESC = [ globalContext ];
ESC.push(functionContext<firstFunction>);
ESC.push(functionContext<secondFunction>);
ESC.pop(); // secondFunction();
ESC.pop(); // firstFunction();
// globalContext, 直到程序結束才被移除
複製代碼
總結
簡稱 VO(Variable object),是與執行上下文相關的數據域,存儲了在上下文中定義的變量和函數聲明。
咱們已經知道執行上下文分全局執行上下文和函數執行上下文,既然執行上下文都有變量對象,那對應就有全局執行上下文變量對象和函數執行上下文變量對象。
這個名字有點長,可是若是叫全局對象,你確定就以爲熟悉,其實二者是一回事,可使用 this 來訪問(瀏覽器環境)。
瀏覽器環境 this 指向 window,node 環境 this 指向 global。
變量對象咱們知道是 VO,函數執行上下文變量對象能夠叫活動對象(activation object,這裏簡稱 AO),這麼稱呼的緣由是當進入函數執行上下文中,這個執行上下文才被激活,此刻屬性才能被訪問。
咱們知道代碼是一段一段執行的,這裏一段代碼指的是執行上下文,而一個程序中有一個全局執行上下文和無數個函數執行上下文(Eval 函數執行上下文咱們不帶它玩)。因此,一般在函數中,查找變量會先從當前執行上下文的 VO 中查找,若是沒有就到其父級執行上下文的 VO 查找,直至全局執行上下文的 VO。這流程所造成的鏈表就是做用域鏈(Scope Chain)(是否是有點像原型鏈,畢竟都是鏈嘛)。
Javascript 特有的一套玩法,不一樣場景下,指向的對象就不一樣。
apply/call/bind
this - 函數第一個參數(即所綁定對象);第3點中有個函數別名除外,這裏解釋下
var name = 'I am window';
let obj = {
name: 'I am obj',
fn: function(str) {
console.log(str, this.name);
}
}
const fn = obj.fn;
obj.fn('obj.fn =>'); // obj.fn => I am obj
fn('fn =>'); // fn => I am window
複製代碼
一個字,誰調用了,this 就指向誰(誰動我打誰,哈哈)。