Variable Object(變量對象)

上一篇簡單介紹了執行上下文,如今來說講與執行上下文密切相關的Variable Object;node

變量對象的定義:變量對象是一個特殊的對象,而且與執行上下文息息相關,VO(變量對象)裏面會存有下列內容:瀏覽器

  • variables(var,variableDeclaration,arguments);
  • function declarations(FD)
  • function formal parameters

以上這些內容都會保存在變量對象中,而且變量對象又是(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對象將會包含如下屬性:

  • callee —— 指向當前函數
  • length——當函數調用時,傳遞過來的參數(實參)的數量;注意這裏不是函數定義的時候參數的數量,function.length纔是函數定義時候的參數數量
  • properties-indexes——就是函數調用時傳遞過來的值(實參)會以索引—屬性的形式展示出來
舉例說明:
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);
複製代碼

上下文代碼處理過程分爲兩個階段

  1. 進入執行上下文,可是尚未開始執行該上下文中的代碼(Entering the execution context,but the executable code has not started execute)
  2. 上下文中的代碼執行階段 (code execution)

函數上下文和全局上下文都會有這兩個階段,而且相互獨立,互不影響。

第一階段:進入執行上下文,可是尚未開始執行該上下文中的代碼 在進入執行上下文,可是尚未開始執行該上下文中的代碼的時候,VO會被下列屬性所初始化:

  1. 對於每個函數的形參(若是進入了函數執行上下文)——函數的形參將會成爲VO的屬性,形參的值(函數調用時的傳遞過來的值)就會稱爲該屬性的值,但對於沒有傳遞過來的參數,其形參的值就會被賦予undefined,下列舉例說明:
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)}>
}
複製代碼
  1. 對於每個變量聲明(函數表達式也是變量聲明),所聲明的變量將會稱爲VO的屬性,其值會被賦予成undefined。可是若是變量名與以前的形參名或者函數聲明時的函數名相同,那麼以後的變量聲明並不會破壞已經存在的屬性(也即不會被後面的同名變量所覆蓋)
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? 分析過程:

  1. 首先進入全局執行上下文時,會對VO進行初始化,此時因爲在全局上下文中(就不會檢查形參),會先檢查函數聲明,接着是變量聲明;以前的第三條規則說過若是變量名與以前的形參名或者函數聲明時的函數名相同,那麼以後的變量聲明並不會修改已經存在的屬性(也即不會被後面的同名變量所覆蓋)
此時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,函數內部能夠訪問函數外部的變量,可是函數外部卻不能訪問函數內部的變量(閉包除外),但你知道爲何會這樣嗎?下一篇中的做用域將會爲你解釋這個疑惑

相關文章
相關標籤/搜索