map是JSObject的一個重要屬性,存放一個對象的所有的屬性的入口。要想了解map,就需要打開jsobj.h文件,看裏面的定義。
struct JSObject {
JSObjectMap *map;
jsval *slots;
};
很自然的,我們還要找到JSObjectMap的定義,它也在jsobj.h文件中
struct JSObjectMap {
jsrefcount nrefs; /* count of all referencing objects */
JSObjectOps *ops; /* high level object operation vtable */
uint32 nslots; /* length of obj->slots vector */
uint32 freeslot; /* index of next free obj->slots element */
};
在這個定義中,最重要的信息隱含在註釋中,第一個是對ops的註釋,我們會看到ops其實是一個virtual table。這表示,對象的所有的屬性名,都存放在這個vtable中;第2個信息是對nslots的註釋,它提及obj->slots其實是一個vector。
這如同在告訴我們,我們在定義一個對象的屬性時,屬性的名稱和類型等信息,會通過ops增加到屬性表中,屬性的值會對應放到slots這個vector中。例如:
var obj = new Object();
obj.a = 5;
那麼“a”這個屬性的類型是整型的,slots中增加了個新的值5。仔細看去,ops是JSObjectOps這個結構體中,我們可以在jsapi.h中找到這個結構體的定義:
struct JSObjectOps {
/* Mandatory non-null function pointer members. */
JSNewObjectMapOp newObjectMap;
JSObjectMapOp destroyObjectMap;
JSLookupPropOp lookupProperty;
JSDefinePropOp defineProperty;
JSPropertyIdOp getProperty;
JSPropertyIdOp setProperty;
JSAttributesOp getAttributes;
JSAttributesOp setAttributes;
JSPropertyIdOp deleteProperty;
JSConvertOp defaultValue;
JSNewEnumerateOp enumerate;
JSCheckAccessIdOp checkAccess;
/* Optionally non-null members start here. */
JSObjectOp thisObject;
JSPropertyRefOp dropProperty;
JSNative call;
JSNative construct;
JSXDRObjectOp xdrObject;
JSHasInstanceOp hasInstance;
JSSetObjectSlotOp setProto;
JSSetObjectSlotOp setParent;
JSMarkOp mark;
JSFinalizeOp clear;
JSGetRequiredSlotOp getRequiredSlot;
JSSetRequiredSlotOp setRequiredSlot;
};
我們可以簡單的瀏覽一下其中的函數指針名稱,創建object的map,定義屬性,刪除屬性,給屬性設置值或者獲取屬性值。。。。閉上眼睛想一想,這其中的函數指針是如此之多,所以一個對象的屬性的類型判斷(比如是整型還是String還是其他的)或者是屬性名或者屬性值的存放,都是通過預先定義好的指針函數來完成。
我們甚至可以用一個新的示意圖來表示這一切:
我們可以這樣去想,有一個table,裏面存放對象的屬性列表。根據ruby hacking guide的說法,我們可以想像,有一張大的表,存放所有的對象的屬性,也可能每個對象有自己的屬性表。但,這屬於具體實現的範疇,我們只要明白每個對象有屬於自己的屬性表,對錶格中的行或者列進行操作時,是由JSObjectOps這個結構體中的指針函數來完成的就可以了。
/*
* Share proto's map only if it has the same JSObjectOps, and only if
* proto's class has the same private and reserved slots as obj's map
* and class have. We assume that if prototype and object are of the
* same class, they always have the same number of computed reserved
* slots (returned via clasp->reserveSlots); otherwise, prototype and
* object classes must have the same (null or not) reserveSlots hook.
*/
if (proto &&
(map = proto->map)->ops == ops &&
((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp ||
(!((protoclasp->flags ^ clasp->flags) &
(JSCLASS_HAS_PRIVATE |
(JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) &&
protoclasp->reserveSlots == clasp->reserveSlots)))
{
/*
* Default parent to the parent of the prototype, which was set from
* the parent of the prototype's constructor.
*/
if (!parent)
parent = OBJ_GET_PARENT(cx, proto);
/* Share the given prototype's map. */
obj->map = js_HoldObjectMap(cx, map);
/* Ensure that obj starts with the minimum slots for clasp. */
nslots = JS_INITIAL_NSLOTS;
} else {
/* Leave parent alone. Allocate a new map for obj. */
map = ops->newObjectMap(cx, 1, ops, clasp, obj);
if (!map)
goto bad;
obj->map = map;
/* Let ops->newObjectMap set nslots so as to reserve slots. */
nslots = map->nslots;
}
Ok,我們在上回書說到有若干的判斷條件,就是爲了創建一個map,並設置爲obj->map的屬性――不管是用parent的原型來創建,還是單獨創建新的map(if 。。。else 部分),最終都是爲了這個目的。
/* Allocate a slots vector, with a -1'st element telling its length. */
newslots = AllocSlots(cx, NULL, nslots);
if (!newslots) {
js_DropObjectMap(cx, obj->map, obj);
obj->map = NULL;
goto bad;
}
/* Set the proto, parent, and class properties. */
newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
/* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */
for (i = JSSLOT_CLASS + 1; i < nslots; i++)
newslots[i] = JSVAL_VOID;
/* Store newslots after initializing all of 'em, just in case. */
obj->slots = newslots;
再看newObject中剩餘的代碼,真是容易多了,既然創建了map來存放屬性名了,那麼我們就要對應屬性名來存放存放初始的屬性值了。我們可以看到,每個創建的GC對象,都會被預先設置3個內置的屬性,proto,parent和class。然後,對剩餘的slot,會設置undefined(JSVal_VOID會被解釋成Undefined)。
到現在,我們的Global內置對象就已經初步創建完成了。