JavaScript深入之變量對象

JavaScript深入之變量對象

當 JavaScript 代碼執行一段可執行代碼(executable code)時,會創建對應的執行上下文(execution context)。

對於每個執行上下文,都有三個重要屬性:

  • 變量對象(Variable object,VO)
  • 作用域鏈(Scope chain)
  • this

變量對象

變量對象是與執行上下文相關的數據作用域,存儲了在上下文中定義的變量和函數聲明。

因爲不同執行上下文下的變量對象稍有不同,來看全局上下文下的變量對象和函數上下文下的變量對象。

全局上下文

全局對象是預定義的對象,作爲 JavaScript 的全局函數和全局屬性的佔位符。通過使用全局對象,可以訪問所有其他所有預定義的對象、函數和屬性。

在頂層 JavaScript 代碼中,可以用關鍵字 this 引用全局對象。因爲全局對象是作用域鏈的頭,這意味着所有非限定性的變量和函數名都會作爲該對象的屬性來查詢。

例如,當JavaScript 代碼引用 parseInt() 函數時,它引用的是全局對象的 parseInt 屬性。全局對象是作用域鏈的頭,還意味着在頂層 JavaScript 代碼中聲明的所有變量都將成爲全局對象的屬性。

  • 可以通過 this 引用,在客戶端 JavaScript 中,全局對象就是 Window 對象。

    console.log(this);複製代碼
    
  • 全局對象是由 Object 構造函數實例化的一個對象。

    console.log(this instanceof Object);複製代碼
    
  • 預定義了一堆,嗯,一大堆函數和屬性。

    // 都能生效
    console.log(Math.random());
    console.log(this.Math.random());複製代碼
    
  • 作爲全局變量的宿主。

    var a = 1;
    console.log(this.a);複製代碼
    
  • 客戶端 JavaScript 中,全局對象有 window 屬性指向自身。

    var a = 1;
    console.log(window.a);
    
    this.window.b = 2;
    console.log(this.b);
    

全局上下文中的變量對象就是全局對象。

函數上下文

在函數上下文中,我們用活動對象(activation object, AO)來表示變量對象。

活動對象和變量對象其實是一個東西,只是變量對象是規範上的或者說是引擎實現上的,不可在 JavaScript 環境中訪問,只有到當進入一個執行上下文中,這個執行上下文的變量對象纔會被激活,所以才叫 activation object 吶,而只有被激活的變量對象,也就是活動對象上的各種屬性才能被訪問。

活動對象是在進入函數上下文時刻被創建的,它通過函數的 arguments 屬性初始化。arguments 屬性值是 Arguments 對象。

執行過程

執行上下文的代碼會分成兩個階段進行處理:分析和執行,我們也可以叫做:

  1. 進入執行上下文
  2. 代碼執行

進入執行上下文

當進入執行上下文時,這時候還沒有執行代碼

變量對象會包括:

  • 函數的所有形參 (如果是函數上下文)

    • 由名稱和對應值組成的一個變量對象的屬性被創建

    • 沒有實參,屬性值設爲 undefined

  • 函數聲明

    • 由名稱和對應值(函數對象(function-object))組成一個變量對象的屬性被創建

    • 如果變量對象已經存在相同名稱的屬性,則完全替換這個屬性

  • 變量聲明

    • 由名稱和對應值(undefined)組成一個變量對象的屬性被創建;

    • 如果變量名稱跟已經聲明的形式參數或函數相同,則變量聲明不會干擾已經存在的這類屬性

舉個例子:

function foo(a) {
  var b = 2;
  function c() {}
  var d = function() {};

  b = 3;

}

foo(1);複製代碼

在進入執行上下文後,這時候的 AO 是:

AO = {
    arguments: {
        0: 1,
        length: 1
    },
    a: 1,
    b: undefined,
    c: reference to function c(){},
    d: undefined
}

代碼執行

在代碼執行階段,會順序執行代碼,根據代碼,修改變量對象的值

還是上面的例子,當代碼執行完後,這時候的 AO 是:

AO = {
    arguments: {
        0: 1,
        length: 1
    },
    a: 1,
    b: 3,
    c: reference to function c(){},
    d: reference to FunctionExpression "d"
}

到這裏變量對象的創建過程就介紹完了,讓我們簡潔的總結我們上述所說:

  1. 全局上下文的變量對象初始化是全局對象
  2. 函數上下文的變量對象初始化只包括 Arguments 對象
  3. 在進入執行上下文時會給變量對象添加形參、函數聲明、變量聲明等初始的屬性值
  4. 在代碼執行階段,會再次修改變量對象的屬性值

練習:

console.log(foo);

function foo(){
    console.log("foo");
}

var foo = 1;

會打印函數,而不是 undefined 。

function foo() {
    console.log(a);
    a = 1;
}

foo(); // Uncaught ReferenceError: a is not defined

function bar() {
    a = 1;
    console.log(a);
}
bar(); // 1

轉自:link

發佈了37 篇原創文章 · 獲贊 88 · 訪問量 6218
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章