ECMAScript 認(rèn)可兩類對象:原生(Native)對象和宿主(Host)對象,其中宿主對象包含一個被稱為內(nèi)置對象的原生對象的子類(ECMA 262 3rd Ed Section 4.3)。原生對象屬于語言,而宿主對象由環(huán)境提供,比如說可能是文檔對象、DOM 等類似的對象。
原生對象具有松散和動態(tài)的命名屬性(對于某些實(shí)現(xiàn)的內(nèi)置對象子類別而言,動態(tài)性是受限的--但這不是太大的問題)。對象的命名屬性用于保存值,該值可以是指向另一個對象(Objects)的引用(在這個意義上說,函數(shù)也是對象),也可以是一些基本的數(shù)據(jù)類型,比如:String、Number、Boolean、Null 或 Undefined。其中比較特殊的是 Undefined 類型,因?yàn)榭梢越o對象的屬性指定一個 Undefined 類型的值,而不會刪除對象的相應(yīng)屬性。而且,該屬性只是保存著 undefined
值。
下面簡要介紹一下如何設(shè)置和讀取對象的屬性值,并最大程度地體現(xiàn)相應(yīng)的內(nèi)部細(xì)節(jié)。
對象的命名屬性可以通過為該命名屬性賦值來創(chuàng)建,或重新賦值。即,對于:
var objectRef = new Object(); //創(chuàng)建一個普通的 javascript 對象。
可以通過下面語句來創(chuàng)建名為 “testNumber” 的屬性:
objectRef.testNumber = 5;/* - 或- */objectRef["testNumber"] = 5;
在賦值之前,對象中沒有“testNumber” 屬性,但在賦值后,則創(chuàng)建一個屬性。之后的任何賦值語句都不需要再創(chuàng)建這個屬性,而只會重新設(shè)置它的值:
objectRef.testNumber = 8;/* - 或- */objectRef["testNumber"] = 8;
稍后我們會介紹,Javascript 對象都有原型(prototypes)屬性,而這些原型本身也是對象,因而也可以帶有命名的屬性。但是,原型對象命名屬性的作用并不體現(xiàn)在賦值階段。同樣,在將值賦給其命名屬性時,如果對象沒有該屬性則會創(chuàng)建該命名屬性,否則會重設(shè)該屬性的值。
當(dāng)讀取對象的屬性值時,原型對象的作用便體現(xiàn)出來。如果對象的原型中包含屬性訪問器(property accessor)所使用的屬性名,那么該屬性的值就會返回:
/* 為命名屬性賦值。如果在賦值前對象沒有相應(yīng)的屬性,那么賦值后就會得到一個:*/objectRef.testNumber = 8;/* 從屬性中讀取值 */var val = objectRef.testNumber;/* 現(xiàn)在, - val - 中保存著剛賦給對象命名屬性的值 8*/
而且,由于所有對象都有原型,而原型本身也是對象,所以原型也可能有原型,這樣就構(gòu)成了所謂的原型鏈。原型鏈終止于鏈中原型為 null 的對象。Object
構(gòu)造函數(shù)的默認(rèn)原型就有一個 null 原型,因此:
var objectRef = new Object(); //創(chuàng)建一個普通的 JavaScript 對象。
創(chuàng)建了一個原型為 Object.prototype
的對象,而該原型自身則擁有一個值為 null 的原型。也就是說,objectRef
的原型鏈中只包含一個對象-- Object.prototype
。但對于下面的代碼而言:
/* 創(chuàng)建 - MyObject1 - 類型對象的函數(shù)*/function MyObject1(formalParameter){ /* 給創(chuàng)建的對象添加一個名為 - testNumber - 的屬性并將傳遞給構(gòu)造函數(shù)的第一個參數(shù)指定為該屬性的值:*/this.testNumber = formalParameter;} /* 創(chuàng)建 - MyObject2 - 類型對象的函數(shù)*/function MyObject2(formalParameter){ /* 給創(chuàng)建的對象添加一個名為 - testString - 的屬性并將傳遞給構(gòu)造函數(shù)的第一個參數(shù)指定為該屬性的值:*/this.testString = formalParameter;} /* 接下來的操作用 MyObject1 類的實(shí)例替換了所有與 MyObject2 類的實(shí)例相關(guān)聯(lián)的原型。而且,為 MyObject1 構(gòu)造函數(shù)傳遞了參數(shù)- 8 - ,因而其 - testNumber - 屬性被賦予該值:*/MyObject2.prototype = new MyObject1( 8 ); /* 最后,將一個字符串作為構(gòu)造函數(shù)的第一個參數(shù),創(chuàng)建一個 - MyObject2 - 的實(shí)例,并將指向該對象的引用賦給變量 - objectRef - :*/var objectRef = new MyObject2( "String_Value" );
被變量 objectRef
所引用的 MyObject2
的實(shí)例擁有一個原型鏈。該鏈中的第一個對象是在創(chuàng)建后被指定給 MyObject2
構(gòu)造函數(shù)的 prototype 屬性的 MyObject1
的一個實(shí)例。MyObject1
的實(shí)例也有一個原型,即與 Object.prototype
所引用的對象對應(yīng)的默認(rèn)的 Object 對象的原型。最后, Object.prototype
有一個值為 null 的原型,因此這條原型鏈到此結(jié)束。
當(dāng)某個屬性訪問器嘗試讀取由 objectRef
所引用的對象的屬性值時,整個原型鏈都會被搜索。在下面這種簡單的情況下:
var val = objectRef.testString;
因?yàn)?nbsp;objectRef
所引用的 MyObject2
val
。但是:
var val = objectRef.testNumber;
則不能從 MyObject2
實(shí)例自身中讀取到相應(yīng)的命名屬性值,因?yàn)樵搶?shí)例沒有這個屬性。然而,變量 val
的值仍然被設(shè)置為 8
,而不是未定義--這是因?yàn)樵谠搶?shí)例中查找相應(yīng)的命名屬性失敗后,解釋程序會繼續(xù)檢查其原型對象。而該實(shí)例的原型對象是 MyObject1
的實(shí)例,這個實(shí)例有一個名為“testNumber”的屬性并且值為 8
,所以這個屬性訪問器最后會取得值 8
。而且,雖然MyObject1
和 MyObject2
都沒有定義 toString
方法,但是當(dāng)屬性訪問器通過 objectRef
讀取 toString
屬性的值時:
var val = objectRef.toString;
變量 val
也會被賦予一個函數(shù)的引用。這個函數(shù)就是在 Object.prototype
的 toString
屬性中所保存的函數(shù)。之所以會返回這個函數(shù),是因?yàn)榘l(fā)生了搜索objectRef
原型鏈的過程。當(dāng)在作為對象的 objectRef
中發(fā)現(xiàn)沒有“toString”屬性存在時,會搜索其原型對象,而當(dāng)原型對象中不存在該屬性時,則會繼續(xù)搜索原型的原型。而原型鏈中最終的原型是Object.prototype
,這個對象確實(shí)有一個 toString
方法,因此該方法的引用被返回。
最后:
var val = objectRef.madeUpProperty;
返回 undefined
,因?yàn)樵谒阉髟玩湹倪^程中,直至 Object.prototype
的原型--null,都沒有找到任何對象有名為“madeUpPeoperty”的屬性,因此最終返回 undefined
。
不論是在對象或?qū)ο蟮脑椭校x取命名屬性值的時候只返回首先找到的屬性值。而當(dāng)為對象的命名屬性賦值時,如果對象自身不存在該屬性則創(chuàng)建相應(yīng)的屬性。
這意味著,如果執(zhí)行像 objectRef.testNumber = 3
這樣一條賦值語句,那么這個 MyObject2
的實(shí)例自身也會創(chuàng)建一個名為“testNumber”的屬性,而之后任何讀取該命名屬性的嘗試都將獲得相同的新值。這時候,屬性訪問器不會再進(jìn)一步搜索原型鏈,但 MyObject1
實(shí)例值為 8
的“testNumber”屬性并沒有被修改。給 objectRef
對象的賦值只是遮擋了其原型鏈中相應(yīng)的屬性。
注意:ECMAScript 為 Object 類型定義了一個內(nèi)部 [[prototype]]
屬性。這個屬性不能通過腳本直接訪問,但在屬性訪問器解析過程中,則需要用到這個內(nèi)部 [[prototype]]
屬性所引用的對象鏈--即原型鏈。可以通過一個公共的 prototype
屬性,來對與內(nèi)部的 [[prototype]]
屬性對應(yīng)的原型對象進(jìn)行賦值或定義。這兩者之間的關(guān)系在 ECMA 262(3rd edition)中有詳細(xì)描述,但超出了本文要討論的范疇。