上一篇簡單介紹了執行上下文,如今來說講與執行上下文密切相關的Variable Object;node
變量對象的定義:變量對象是一個特殊的對象,而且與執行上下文息息相關,VO(變量對象)裏面會存有下列內容:瀏覽器
以上這些內容都會保存在變量對象中,而且變量對象又是(Execution Context)執行上下文的屬性:bash
ExecutionContext = {
VariableObject : {
//這裏保存了var聲明的變量,FD,function arguments,以及函數形參
}
}
複製代碼
下面的變量對象都用VO來代替閉包
只有全局上下文中的變量對象容許經過VO的屬性名稱間接訪問(全局對象自身就是變量對象);對於其餘的上下文直接去引用變量對象是不可能的,純粹是一種內部的實現機制。稍後會有說明函數
當咱們聲明一個變量或者函數的時候,就會成爲VO對象新的屬性,而且所聲明的變量值會稱爲新的屬性值,for example:學習
var a = 5;
function foo(x) {
var b = 10;
}
foo(20);
複製代碼
上面說過每個執行上下文中都會有一個變量對象:ui
全局執行上下文中的變量對象
VO(globalContext) = {
a: 5;
foo: reference function foo
}
foo函數執行上下文中的變量對象
VO(foo function context) ={
x: 20,
b: 10,
arguments: {
0:20,
length:1,
callee: <reference> function foo
}
}
複製代碼
在不一樣執行上下文中的變量對象 函數上下文中能夠在VO上定義額外的細節(例如arguments) spa
全局執行上下文中的變量對象先來看看全局對象的定義:全局對象在進入任何執行上下文中就會被建立,該對象的屬性能夠在單一副本程序的任何地方訪問到它,全局對象的生命週期是隨着程序的運行結束而結束;3d
全局對象在被建立的時候會初始化包含下列屬性,例如Math,String,Date等等;一樣的也會建立一個額外的對象指向自身,例如在瀏覽器對象模型中,會建立一個window對象而且指向global object;在node.js中就是指的global對象code
global = {
Math: <...>,
String: <...>,
.....
.....
window: global
}
複製代碼
例如:
var a = 6;
console.log(a) //6
console.log(window.a) //6
console.log(a === window.a) //true
複製代碼
以前已經說過在全局執行上下文中的VO(變量對象)就是全局對象
VO(globalContext) === global;
複製代碼
以前也說過在全局執行上下文中聲明的變量,能夠間接的經過global object的屬性來得到:
var a = 'hello';
console.log(a) //'hello',這是直接在全局上下文中找到的
console.log(window.a) //'hello',這是間接經過global找到的,由於在瀏覽器中,會有一個window對象,而且仍是指向global
複製代碼
在node.js中,能夠看到變量是掛載到全局對象global上面的:
函數上下文中的變量對象函數上下文中的(VO)變量對象是不能直接得到的,就好像在學習原型的時候,[[proto]]屬性也是不能直接獲取的,只不過瀏覽器廠商使用__proto__屬性來模擬它;當函數被調用時,函數上下文中的VO也成爲AO(活動對象)。
VO(functionContext) === AO
複製代碼
當進入函數上下文,AO就會被建立,而且會被arguments屬性所初始化,arguments的值指向一個對象。
AO = {
arguments: object
}
複製代碼
其中arguments對象將會包含如下屬性:
舉例說明:
function foo(a,b,c) {
console.log(arguments)
console.log(arguments.length === 2) //true
console.log(foo.length) //3 注意與上一行進行區分
console.log(arguments.callee === foo) //true
}
foo(1,2);
複製代碼
上下文代碼處理過程分爲兩個階段
函數上下文和全局上下文都會有這兩個階段,而且相互獨立,互不影響。
第一階段:進入執行上下文,可是尚未開始執行該上下文中的代碼 在進入執行上下文,可是尚未開始執行該上下文中的代碼的時候,VO會被下列屬性所初始化:
function fn(a,b,c) {
}
foo(1,2)
進入函數執行上下文,可是尚未開始執行函數體裏面的代碼
VO={
a:1,
b:2,
c: undefined, //在函數調用時,並無給參數c傳遞值,那麼就會被賦予爲undefined
arguments: {
0:1,
1:2,
length:2,
callee: <reference> function fn
}
}
複製代碼
2.對於每個函數聲明,也會保存在VO中,可是以後的同名函數聲明會覆蓋以前的,也就是說VO中會保存最新的函數聲明,下列舉例說明:
function foo(a,b) {
console.log(a+b)
}
function foo(a,b,c) {
console.log(a+b+c)
}
當進入全局執行上下文時,VO會進行初始化,第二個同名函數聲明會覆蓋以前的函數聲明
VO = {
foo: reference <function foo(a,b,c) {console.log(a+b+c)}>
}
複製代碼
function fn(a,b,c) {
var d = 30;
function foo(){};
var test = function {}
}
fn(1,2);
當進入函數fn的上下文時,VO會進行初始化
VO = {
a: 1,
b: 2,
c: undefined,
foo: reference function foo,
d: undefined,
test: undefined,
arguments: {
0:1,
1:2,
length:2,
callee: <reference> function fn
}
}
複製代碼
注意:若是進入函數上下文中會先檢查形參,而後是函數聲明(不是函數表達式),最後是其餘變量
第二階段:代碼執行階段 到這裏爲止,VO已經初始化了一些屬性,可是仍是有一部分值並非咱們所但願的(初始值被賦予了undefined)。 隨着代碼的執行,VO中屬性的值會逐漸被修改。
上述例子中的VO會被修改爲:
VO = {
a: 1,
b: 2,
c: undefined,
foo: reference function foo,
d: 30,
test: <reference> function,
arguments: {
0:1,
1:2,
length:2,
callee: <reference> function fn
}
}
複製代碼
下面舉一個經典的例子
alert(a); //function
var a = 1;
alert(a); //1
a = 10;
function a() {};
alert(a); //10
複製代碼
爲何第一個a的值爲function,不是1或10或undefined? 分析過程:
此時VO = {
a: reference function a //這裏並非undefined,不會被覆蓋
}
複製代碼
2.代碼執行階段——當上述代碼執行到第三行時,VO會被修改爲:
VO = {
a: 1
}
故a彈出來爲1
複製代碼
3.當代碼執行到倒數第二行時,VO會被修改爲:
VO = {
a: 10
}
故a彈出來爲10
複製代碼
var a = 10;
function fn(){
console.log(a); //10
}
複製代碼
咱們都知道a的值打印出來是10,函數內部能夠訪問函數外部的變量,可是函數外部卻不能訪問函數內部的變量(閉包除外),但你知道爲何會這樣嗎?下一篇中的做用域將會爲你解釋這個疑惑