深刻學習js系列是本身階段性成長的見證,但願經過文章的形式更加嚴謹、客觀地梳理js的相關知識,也但願可以幫助更多的前端開發的朋友解決問題,期待咱們的共同進步。javascript
若是以爲本系列不錯,歡迎點贊、評論、轉發,您的支持就是我堅持的最大動力。前端
上一篇文章重點介紹的是執行山下文棧,當javascript代碼執行一段可執行的代碼(executable code)時候,會建立對應的執行上下文(execution context)。java
對於每個執行上下文,都有三個重要的屬性:微信
- 變量對象(Variable object VO)
- 做用域鏈(Scope chain)
- this
本文重點介紹建立變量對象的過程。閉包
變量對象是與執行上下文相關的數據做用域,存儲了再上下文中定義的變量和函數聲明。dom
由於不一樣執行山下文下的變量對象稍有不一樣,因此咱們來聊聊全局上下文下的變量對象和函數上下文下的變量對象。函數
全局上下文post
咱們先了解一個概念,叫作全局對象。在W3CSchool中也有介紹:學習
全局對象是預約義的對象,做爲 JavaScript 的全局函數和全局屬性的佔位符。經過使用全局對象,能夠訪問全部其餘全部預約義的對象、函數和屬性。ui
在頂層 JavaScript 代碼中,能夠用關鍵字 this 引用全局對象。由於全局對象是做用域鏈的頭,這意味着全部非限定性的變量和函數名都會做爲該對象的屬性來查詢。
例如,當JavaScript 代碼引用 parseInt() 函數時,它引用的是全局對象的 parseInt 屬性。全局對象是做用域鏈的頭,還意味着在頂層 JavaScript 代碼中聲明的全部變量都將成爲全局對象的屬性。
若是感受全局對象有些看不懂的話,容我再來介紹一下全局對象:
1.能夠經過this引用,在客戶端javascript中,全局對象就是window對象。
console.log(this); // window
複製代碼
2.全局對象是由Object 構造函數實例化的一個對象。
console.log(this instanceof Object); // true
複製代碼
3.預約義一堆,恩,一大堆的函數和屬性。
// /都能生效
console.log(Math.random());
console.log(this.Math.random());
複製代碼
4.做爲全局變量的宿主。
var a = 1;
console.log(this.a);
複製代碼
5.客戶端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對象。
執行上下文的代碼會分紅兩個階段進行處理:分析和執行,咱們也能夠叫作: 一、進入執行山下文 二、代碼執行
進入執行上下文的時候,這個時候尚未執行代碼,
變量對象包括: 一、函數的全部形參(如何是函數上下文)
* 由名稱和對應的值組成一個變量對象的屬性被建立
* 沒有實參,屬性值設置爲 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"
}
複製代碼
到這裏變量對象的建立過程就介紹完了,讓咱們簡潔的總結咱們上述所說:
最後咱們看幾個例子:
第一題:
function foo(){
console.log(a);
a = 1;
}
foo(); // ??
function bar(){
a = 1;
console.log(a);
}
bar(); // ??
複製代碼
第一段會報錯:Uncaught ReferenceError: a is not defined
。 第二段會打印:1
。
這是由於函數中的 "a" 並無經過 var 關鍵字聲明,全部不會被存放在 AO 中。
第一段執行 console 的時候, AO 的值是:
AO = {
arguments: {
length: 0
}
}
複製代碼
沒有 a 的值,而後就會到全局去找,全局也沒有,因此會報錯。
當第二段執行 console 的時候,全局對象已經被賦予了 a 屬性,這時候就能夠從全局找到 a 的值,因此會打印 1。
第二題:
console.log(foo);
function foo(){
console.log("foo");
}
var foo = 1;
複製代碼
會打印函數,而不是 undefined 。
這是由於在進入執行上下文時,首先會處理函數聲明,其次會處理變量聲明,若是若是變量名稱跟已經聲明的形式參數或函數相同,則變量聲明不會干擾已經存在的這類屬性。
歡迎添加個人我的微信討論技術和個體成長。
歡迎關注個人我的微信公衆號——指尖的宇宙,更多優質思考乾貨