執行上下文和執行棧

王振宇發表於2019-03-21

什麼是執行上下文?

個人理解:當前JavaScript程式碼解析和執行時候的環境就是執行上下文

執行上下文共有三種型別:

全域性執行上下文:只有一個,JavaScript程式碼載入的時候就會進入這個環境,然後建立瀏覽器中的全域性物件(window),全域性上下文的this指向window

函式執行上下文:有很多個,每個函式被呼叫的時候都會建立一個該函式的執行上下文

Eval執行上下文:Eval函式執行時的上下文

因為Eval函式存在效能及安全問題,所以一般不建議使用,本文主要討論全域性執行上下文及函式執行上下文

執行上下文的建立分為兩個階段:

1. 建立階段

2. 執行階段

建立階段:確定this及作用域鏈,宣告函式並初始化,宣告變數並賦予預設值undefined,建立arguments物件

執行階段:變數賦值並執行其它程式碼

注:上述建立階段變數宣告指var宣告的變數,所以可以在變數宣告前訪問該變數(undefined),

如果是let或者const宣告的變數,只有執行了let活const程式碼後該變數才可以訪問,這是因為

let或const宣告的變數,不存在變數提升。而且要求必須 等let宣告語句執行完之後,變數才能使用,不然會報Uncaught ReferenceError錯誤。ES6 明確規定,如果區塊中存在let和const命令,這個區塊對這些命令宣告的變數,從一開始就形成了封閉作用域。凡是在宣告之前就使用這些變數,就會報錯。總之,在程式碼塊內,使用let或const命令宣告變數之前,該變數都是不可用的。

看木易楊的文章講建立階段還會分為

1. 確定this,也被稱為This Binding

2. 詞法環境元件建立

3. 變數環境元件建立

對這一塊的理解還不夠,故本文不做擴充,有興趣的朋友可以檢視原文連結:

github.com/yygmind/blo…或自行查閱相關資料。

什麼是執行棧?

個人理解:執行棧就是一個儲存JavaScript程式碼執行上下文的棧,既然是棧,所以具有 LIFO(後進先出)結構,也就是JavaScript程式碼載入的時候,全域性執行上下文入棧,然後執行函式時對應的函式執行山下文入棧,函式執行結束,該函式執行上下文出棧,最後頁面關閉的時候,全域性執行上下文出棧。例如下程式碼:

function foo(){
  function bar(){
    console.log('bar');
  };
  bar();
}
foo();
複製程式碼

首先全域性上下文入棧

執行上下文和執行棧

foo執行上下文入棧

執行上下文和執行棧

bar執行上下文入棧

執行上下文和執行棧

然後就是bar執行完畢,bar執行上下文出棧,foo執行上下文出棧,頁面關閉,全域性執行上下文出棧。

理解了執行棧,我們來看一個小例子:

function printN1(n){
  for(let i = 1;i<=n;i++){
    console.log(i);
  }
}
printN1(10);
function printN2(n){
  if(n){
    console.log(n);
    n -= 1;
    printN2(n);
  }
}
printN2(10);
複製程式碼

上面兩個程式碼,都是列印<=n的正整數,如果忽略列印順序的問題,哪一種實現更好呢,或者說差的那一種有什麼問題呢?如果n比較小的時候你沒有感覺到什麼差異,那把n改為10000呢?

如果有錯誤或者不嚴謹的地方,請給予指正,十分感謝!


相關文章