上下文詳解(持續更新)

yaoer1994發表於2018-04-14

提醒:執行上下文和作用域是完完全全不同的兩個概念,請勿混淆視聽,請勿混淆視聽,請勿混淆視聽。請看下面?

JavaScript程式碼的整個執行過程,分為兩個階段,程式碼編譯階段與程式碼執行階段。編譯階段由編譯器完成,將程式碼翻譯成可執行程式碼,這個階段(編譯階段)作用域規則會確定。執行階段由引擎完成,主要任務是執行可執行程式碼,執行上下文在這個階段(執行階段)建立。不過,作用域鏈是在上下文生命週期的建立階段被確定。唔。。。。

何為上下文?簡單地說,就是程式碼在其內部中執行的物件,不是作用域!請看上面?。JavaScript編碼中我們通常會使用函式建立一個上下文,函式執行的時候,上下文被啟用為執行上下文。

上下文特點

  1. 類似於棧的讀取方式,具有“先進後出,後進先出”的特點。在A上下文執行中啟用B上下文,執行上下文變成B進入棧頂,B上下文執行完畢後跳出棧,繼續A上下文執行。以此類推。
  2. 同步執行,棧頂的上下文執行中,其他上下文等待。(理解特點1)
  3. 上下文執行完畢跳出棧。
  4. 底層永遠是全域性上下文,視窗關閉全域性上下文跳出棧(這裡需要指出全域性上下文的變數物件,以瀏覽器中為例,全域性物件為window。它的變數物件,就是window物件。而這個特殊,在this指向上也同樣適用,this也是指向window。)
  5. (按照我現在的理解)在瀏覽器中,全域性上下文的生命週期等同於程式的生命週期,只要程式不結束(關掉視窗),那麼全域性上下文永遠存在,在其他的所有上下文環境中,都可以訪問全域性上下文的屬性。

上下文生命週期

一、建立週期及其流程

  1. 建立arguments物件,檢查上下文引數,並將上下文引數建立為arguments物件下的屬性和屬性值
  2. 檢查上下文中的函式宣告(function關鍵字申明),如果上下文中不存在該函式的函式名這樣一個屬性,則在上下文中以函式名建立一個屬性,屬性值指向該函式所在記憶體的引用地址,如果上下文中已經存在該函式名這樣一個屬性,那麼該屬性的屬性值則會被覆蓋為新的引用地址(即該函式所在記憶體的引用地址)。
  3. 檢查上下文中的變數申明,檢查到之後,就會在上下文中建立一個屬性,屬性名就是變數申明的名字,屬性值則是 undifinded ,注意:檢查到以後,會先判斷該屬性值是否存在,如果存在,為了以防同名函式被修改為undefined,那麼則會被跳過

二、執行階段

執行階段就很簡單啦,就是變數賦值、函式引用和執行程式碼咯。

栗子一
function test (){
    var a = 'string'
    function foo (){
        return 'this is a foo'
    },
    a;
    foo()
}
test() 
複製程式碼
  1. 建立階段(建立arguments => 檢查函式宣告 => 檢查變數宣告)
variableObject = {
    arguments:[],
    foo:<foo reference>,//函式引用地址
    a:undefinded
}
複製程式碼
  1. 執行階段(變數賦值、函式引用、執行程式碼)
variableObject = {
    arguments:[],
    foo:'this is a foo',//執行函式引用
    a:'string',//變數賦值
}

複製程式碼

從上面的例子我們可以看出,變數賦值其實是在執行階段,而不是在建立階段,理解好這一點,在建立過程,若非是函式宣告,可以成功的變數宣告全部是undefined。這樣,下面的例子也就可以十分輕鬆地理解透徹

栗子二
function foo() { console.log('function foo') }
var foo = 20;

console.log(foo); // 20
複製程式碼

下面我們來對這個栗子進行分解

  1. 建立階段
variableObject = {
    arguments:[],
    foo:<foo reference>
}
複製程式碼
  1. 執行階段
variableObject = {
    argumrnts:[],
    foo:20,//變數賦值
}
複製程式碼

上面例子,上下文建立階段,在檢查函式宣告階段的時候檢測到函式宣告foo,所以建立foo屬性,並且foo的屬性值指向函式foo所在記憶體的引用地址,然後檢查變數宣告的時候檢測到變數宣告foo,不過這個時候JS解析器檢查到上下文已經存在屬性undefined,所以跳過,在上下文建立階段週期內,foo的值始終指向函式foo所在記憶體的引用地址。接下來就是上下文的執行階段,這個時候執行變數賦值,所以foo被賦值為20。

從上面這個栗子我們也從側面發現,宣告具有提升性,並且函式宣告的優先順序大於變數宣告。但是並不是淺顯的理解為foo先被函式宣告為函式foo所在記憶體引用地址,再次被覆蓋為20,整個上下文建立週期,foo值,未被覆蓋。

相關文章