首發地址: https://github.com/jeuino/Blo...
在上一篇《JavaScript 以內存空間》中,簡單介紹了下 JavaScript 中的變量是如何存儲的。本篇文章將總結一下變量和函數在運行時是如何查找並引用的。git
在《JavaScript 之執行上下文》中,咱們知道了一段代碼在開始執行時,首先會建立一個執行上下文。而當進入執行上下文時,就會建立一個變量對象,此時代碼還未執行。github
變量對象是與執行上下文相關的數據做用域,執行上下文中定義的全部變量和函數都保存在這個對象中。能夠將變量對象理解爲做用域這個抽象概念的實體,當代碼執行時,是從變量對象中查找是否存在相應的變量的。它具體是如何查找的呢?咱們繼續往下看。函數
變量對象的建立,依次經歷瞭如下三個過程:ui
變量對象的建立,是優先掃描函數聲明的,若是變量與函數同名,則以函數爲主。下面簡單解釋下緣由:
假設變量對象(做用域)中已經存在一個名爲 foo 的屬性,它表明一個函數引用。當掃描變量聲明時,又遇到一個命名爲 foo 的變量,解釋器會詢問變量對象(做用域)是否已經存在一個該名稱的標識符,若是存在,解釋器會忽略該指令,繼續執行;不然它會要求變量對象聲明一個命名爲 foo 的屬性,並賦值爲 undefined。this
因爲全局執行上下文和函數執行上下文中的變量對象有一些差別,因此下面對它們分別進行介紹。spa
在全局執行上下文中,變量對象初始化是全局對象,也就是 window 對象。所以全部聲明的全局變量和函數都是做爲 window 對象的屬性和方法建立的。3d
全局對象是預約義的對象,做爲 JavaScript 的全局函數和全局屬性的佔位符。經過使用全局對象,能夠訪問其餘全部預約義的對象、函數和屬性。全局對象不是任何對象的屬性,因此它沒有名稱。code
在頂層 JavaScript 代碼中,能夠用關鍵字 this 引用全局對象。對象
由於全局對象是做用域鏈的頭,這意味着在頂層 JavaScript 代碼中聲明的全部變量都將成爲全局對象的屬性。blog
在函數執行上下文中,變量對象就是其活動對象(activation object, AO)。
什麼是活動對象?
變量對象和活動對象實際上是一個對象,二者意思相同,只是處於執行上下文的不一樣生命週期。
執行上下文的生命週期包括兩個階段:建立階段和執行階段。後續會寫一篇文章單獨總結。
變量對象就是在建立階段時初始化的,此時代碼還未執行,變量對象中的屬性不能被訪問。進入執行階段後,開始逐行執行代碼,此時變量對象就會被激活變成活動對象(AO),其各類屬性才能被訪問。此時就能夠經過查找變量對象上是否存在某屬性的方式查找聲明的變量和函數,獲取到引用後進行變量賦值、函數調用等操做。
咱們一塊兒來看下下面這段代碼生成的變量對象是什麼樣子的:
function fn(a, b) { var c = 2; function fn1() {} var d = function () {}; } fn(1, 2, 3);
// fn 執行上下文中的變量對象 VO = { Arguments: { 0: 1, 1: 2, 2: 3, length: 3 }, a: 1, b: 2, c: undefined, fn1: <fn1 reference>, d: undefined }
此時的變量對象是在代碼執行前建立的,變量對象中的屬性都不能訪問。在進入執行階段以後,變量對象變成了活動對象,此時變量對象的屬性能夠被訪問了。而後開始執行代碼,代碼執行的過程當中可能會修改變量對象的屬性值。
仍是上面的例子,當代碼執行完畢,這時的 AO 變成:
AO = { Arguments: { 0: 1, 1: 2, 2: 3, length: 3 }, a: 1, b: 2, c: 2, fn1: <fn1 reference>, d: <FunctionExpression "d" reference> }
總結:
在每一個執行上下文中,都包括三個重要的屬性:
下篇文章將開始介紹執行上下文中的this
關鍵字,敬請期待。
參考:
JavaScript深刻之變量對象
JavaScript 之深刻理解執行上下文