奇妙的Function和prototype
先来看一段代码
function fn() {}; Function.prototype.test = 'fn'; fn.prototype.test = 'aaa'; var fo = new fn(); alert(fo.test);
相信所有人都知道答案,ok,再来一段:
function fn() { return {}; }; Function.prototype.test = 'fn'; fn.prototype.test = 'aaa'; var fo = new fn(); alert(fo.test);
可能有一部分人被淘汰了,答案是undefined,再来一段:
function fn() { return fn; }; Function.prototype.test = 'fn'; fn.prototype.test = 'aaa'; var fo = new fn(); alert(fo.test);
是什么? 很不幸,'fn',又有一些被淘汰了,那我们继续往前:
function fn() { alert(this.test); return {test: 'bbb'}; }; fn.prototype.test = 'aaa'; var fo = new fn();
结果是'aaa'。很有一部分人冲到了最后,不过,有人能清楚的解释一下,这是为什么呢? 相信绝大多数同学黯然的低下了头。
正当我们为广大劳苦大众发愁的时候,一声春雷响,我们迎来了@richie同学的JavaScript对象模型-执行模型。该文洋洋洒洒,图文并茂的讲述了具体的执行机制,这年头,能如此较真的人还真不多,赞一个。
摘录精华如下:
对象创建过程:
是指形如new Fn(args)的过程
1. 创建一个build-in object对象obj并初始化
2. 如果Fn.prototype是Object类型,则将obj的内部[[Prototype]]设置为Fn.prototype,否则obj的[[Prototype]]将为其初始化值(即Object.prototype)
3. 将obj作为this,使用args参数调用Fn的内部[[Call]]方法
3.1 内部[[Call]]方法创建当前执行上下文
3.2 调用Fn的函数体
3.3 销毁当前的执行上下文
3.4 返回Fn函数体的返回值,如果F的函数体没有返回值则返回undefined
4. 如果[[Call]]的返回值是Object类型,则返回这个值,否则返回obj
注意步骤2中, prototype指对象显示的prototype属性,而[[Prototype]]则代表对象内部Prototype属性(隐式的)。
Firefox中,可以用__proto__获取[[Prototype]]。
函数对象创建过程:
JavaScript代码中定义函数,或者调用Function创建函数时,最终都会以类似这样的形式调用Function函数:var newFun=Function(funArgs, funBody); 。创建函数对象的主要步骤如下:
1. 创建一个build-in object对象fn
2. 将fn的内部[[Prototype]]设为Function.prototype
3. 设置内部的[[Call]]属性,它是内部实现的一个方法,处理逻辑参考对象创建过程的步骤3
4. 设置内部的[[Construct]]属性,它是内部实现的一个方法,处理逻辑参考对象创建过程的步骤1,2,3,4
5. 设置fn.length为funArgs.length,如果函数没有参数,则将fn.length设置为0
6. 使用new Object()同样的逻辑创建一个Object对象fnProto
7. 将fnProto.constructor设为fn
8. 将fn.prototype设为fnProto
9. 返回fn
压轴的模型图:
红色虚线表示[[Prototype]]