书接上回,我们说到global对象的初始创建已经完成了。那,你肯定会问了,为什么说是初始创建,而不是完整的创建呢?要回答这个问题,还要从ECMA262说起:
在ECMA262,强调了Global这个全局函数的很多信息,比如说,这个内置对象是先于Context就存在了的。比方说,在默认的情况下,我们调用的方法,其实都是Global这个对象的方法。比方说 :
var obj = new Object();
其实也就等于
var obj = new this.Object();
像parseInt这样的方法,其实也都是挂在Global名下的。Global对象下,有若干属性,包括Object,Array,Boolean,String…. 这么说你肯定就明白了。其他的内置对象,都是Global的一个属性值。其实,不光内置对象,所有的新定义的function ,也都会在定义后(即通过function关键字)在Global的属性列表中增加一个指向自己的属性。
那么,我们可以想一下,Global和Object之间的关系,真的是很微妙啊。Global.prototype和Object.prototype会是什么样的关系呢?
如果你是一个心急的人,一点会说:“写一段代码不就知道了吗?”。你的程序是不是这样写的?
document.writeln(global.prototype = = …
sorry,忘了告诉你了,global对象是不能这么访问的。你在程序中写:
document.writeln(global);
将会打出undefined.不过,这个Global对象又缺省存在,我们应该怎么去访问它呢?请让我先卖一个关子吧。
有了Global的这个概念以后,我们继续来分析源代码。从part4-part5,我们基本上只在围绕着一个函数做文章,
glob = JS_NewObject(cx, &global_class, NULL, NULL);
这个函数的调用关系讲完之后,我们就要看下面的代码段了。
JS_InitStandardClasses(cx, glob)
JS_DefineFunctions(cx, glob, shell_functions)
从名字上看,我们是要在glob这个对象下,创建并挂接系统内置的对象了(事实也果真如此)。
JS_InitStandardClasses这个函数定义在jsapi.c 这个文件中,我们可以很容易的找到其中的重要部分:
/* Define a top-level property 'undefined' with the undefined value. */
atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID,
NULL, NULL, JSPROP_PERMANENT, NULL)) {
return JS_FALSE;
}
/* Function and Object require cooperative bootstrapping magic. */
if (!js_InitFunctionAndObjectClasses(cx, obj))
return JS_FALSE;
/* Initialize the rest of the standard objects and functions. */
return js_InitArrayClass(cx, obj) &&
js_InitBlockClass(cx, obj) &&
js_InitBooleanClass(cx, obj) &&
js_InitCallClass(cx, obj) &&
js_InitExceptionClasses(cx, obj) &&
js_InitMathClass(cx, obj) &&
。。。。
我在这段代码中,找到的几个处比较重要的函数调用,用红颜色圈了出来。
第一个部分是一个宏:OBJ_DEFINE_PROPERTY,这个宏的主要作用是调用函数指针,来给glob对象创建一个新的属性,属性的类型是JSVAL_VOID,这说明,ECMA262中要求的,Undefined属性是Global的一个属性,已经得到了实现。
第二部分,是我们的主角,弄懂了这个函数,基本上就可以初步理解ECMAScript的对象模型了。而且,它的注释也很有意思,使用了cooperative bootstrapping magic这样的字眼。好的,那就让我们跑到后台去,看看魔术师的鬼把戏是怎么出来的吧。
js_InitFunctionAndObjectClasses仍然在同文件中,所以不用调转就可以找到。
/* If cx has no global object, use obj so prototypes can be found. */
if (!cx->globalObject)
JS_SetGlobalObject(cx, obj);
/* Record Function and Object in cx->resolvingTable, if we are resolving. */
。。。。
/* Initialize the function class first so constructors can be made. */
fun_proto = js_InitFunctionClass(cx, obj);
if (!fun_proto)
goto out;
/* Initialize the object class next so Object.prototype works. */
obj_proto = js_InitObjectClass(cx, obj);
if (!obj_proto) {
fun_proto = NULL;
goto out;
}
/* Function.prototype and the global object delegate to Object.prototype. */
OBJ_SET_PROTO(cx, fun_proto, obj_proto);
if (!OBJ_GET_PROTO(cx, obj))
OBJ_SET_PROTO(cx, obj, obj_proto);
这个程序的开头两大段,做的工作,我猜想我们可以跳过,不知道正不正确,我更想从js_InitFunctionClass(cx, obj)开始(标红的部分)。我们可以看到,我们还是把global当成参数传到了这个函数体中,这样,我们得到的的返回值,就是Function.prototype;同样,如果我们继续向下看,就会看到js_InitObjectClass(cx, obj)这个函数,从返回值、注释加上对函数名的推测,我们会看到,函数运行后返回了Object.prototype。
然后呢,我们留意一下注释,哦,我的老天啊。我的猜测是没有错的Function.prototype和Global对象的prototype被委托给了Object.prototype.这还不够让我们兴奋的吗?
允许兴奋,可不要兴奋大发劲了啊。我们还有正事没有做呢。js_InitFunctionClass还在等着我们去解密呢。
这个函数在jsfun.c中有定义,麻烦你动手先将它找出来。
JSObject *
js_InitFunctionClass(JSContext *cx, JSObject *obj)
{
JSObject *proto;
JSAtom *atom;
JSFunction *fun;
proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
function_props, function_methods, NULL, NULL);
if (!proto)
return NULL;
atom = js_Atomize(cx, js_FunctionClass.name, strlen(js_FunctionClass.name),
0);
if (!atom)
goto bad;
fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, NULL);
if (!fun)
goto bad;
fun->u.i.script = js_NewScript(cx, 1, 0, 0);
if (!fun->u.i.script)
goto bad;
fun->u.i.script->code[0] = JSOP_STOP;
fun->flags |= JSFUN_INTERPRETED;
return proto;
为了简单起见,我将这个函数的调用图先列出来:
js_InitFunctionClass
JS_InitClass
js_Atomize
js_NewObject
js_NewGCThing
js_Atomize
js_NewFunction
js_NewGCThing
那么,我们可以从这个调用图中推导出什么来呢?咱们下回书接着说。