C/C++模拟实现JavaScript原型机制
为了更加深入的理解JavaScript原型机制,我们现在用伪代码来实现,或者说模拟该机制,也许我的理解有所谬误,如果果真如此,还希望不吝赐教。
一、原型机制设计
在JavaScript中,有两个祖先一样的对象:Function.prototype和 Object.prototype。
(1)
Object.prototype:这个对象是所有对象的根,它自己没有爹妈,是女娲造出来的。
(2) Function.prototype:这个对象是所有构造器的根,它事实上也没爹妈,也是女娲造出来的。只不过其内部维护者Object.prototype的引用。
事实上所有对象都维护者一个{prototype}属性,这是一个内部的私有属性,无法通过对象来访问。不过在firefox等浏览器中提供了一个共有属性__proto__来访问这个私有属性。在Js中公有属性名和私有属性名可以相同。但是C/C++中不可以,我们需要做些许改变。而这个女娲分别派出了两位大使MetaObject
和 MetaConstructor,这是C/C++中的类。他们分别负责产生Object.prototype和Function.prototype,(采用单体模式);
1、Object.prototype
(1)通用属性
1.Constructor:对创建对象的函数(构造器)的引用(指针),这里为Object,主要是为了Object.prototype也属于Object类型;设计的需要,实际上不是Object构造的。
2.Prototype:私有的prototype属性,也就是__proto__的值,这里为null。
(2)通用方法
1. hasOwnProperty(property):判断对象是否有某个特定的属性。必须用字符串指定该属性 (例如,
o.hasOwnProperty(”name”))。
2. isPrototypeOf(object):判断该对象是否为另一个对象的原型。
3. propertyIsEnumerable(property):判断给定的属性是否可以用for…in语句进行枚举。
4. toString():返回对象的原始字符串表示。对于Object类,ECMA-262没有定义这个值,所以不同的ECMAScriipt实现具有不同的值。
5. valueOf():返回最适合该对象的原值。对于许多类,该方法返回的值都与toString()的返回值相同。
毕竟Js是动态语言,而C/C++是静态语言,我们实现的只是框架而不涉及到具体细节。
2、Function.prototype
(1)通用属性
1. 0...n属性:这是arguments取参数时的索引,0——n
2. arguments属性:存储存进来所有参数,相当于可变参数参数列表。
3. callee属性:引用当前函数对象,用于函数递归,特别是在匿名函数中特别有用。
4. caller属性:返回调用某个函数的函数对象。
5.constructor属性:Function.prototype的构造器是Function.
6.length属性:当创建一个函数的实例时,函数的 length 属性由脚本引擎初始化为该函数定义中的参数数目。
7.prototype属性:内部{Prototype}——也即__proto__,为Object.prototype。
(2)通用方法
1.apply方法:这个方法允许你使用一个函数,就好像该函数是其他某个对象的函数方法一样。
2.call方法:同apply,只不过参数的传递形式不同。
3.toString方法:将对象转换成字符串。
4.valueOf方法:自己查吧。
我们在此只提供接口并不实现。
二、数据结构
1.MetaObject——单体模式生成Object.prototype
MetaObject { public: static constructor = Object; //事实上并非Object所构造,只不过是为了使Object.prototype属于Object类 static __proto__ = {Prototype}; private: static {Prototype} = null; //假设私有共有变量不冲突public: //一堆接口 }
2. MetaConstructor——单体模式生成Function.prototype
MetaConstructor { public: static constructor = Function; //实际并非Function所构造,只是为了Function.prototype类型为Function static prototype = undefinded; //之所以未定义是因为它不会当成构造器来使用 static __proto__ = {Prototype}; private: static {Prototype} = Object.prototype; public: //一堆接口 }
3.Function——用户可使用的函数构造器的基类
Function { public: static constructor = Function; static prototype = Function.prototype; static __proto__ = {Prototype}; private: static {Prototype} = Function.prototype; }
4.Object——对象基类构造器
Object { public: static constructor = Function; static prototype = Object.prototype; static __proto__ = {Prototype}; private: static {Prototype} = Function.prototype; }
有个统一的公式:若有个对象obj,构造它的的构造器为Constructor,那么:obj.__proto__ = Constructor.prototype;
三、原型链
如有三个类,也即A,B,C(其实就是构造器——函数对象),如B继承A,C继承B,那么就等价于:
function A(){}; //A.prototype = Object.prototype, A.__proto__ = Function.prototype function B(){}; //类推 function C(){}; //类推 B.protype = new A(); C.prototype = new B(); var c = new C();
c中维护者一个B的实例,而这个B的实例中又维护者一个A的实例的引用。等价于:
function A(){}; function B(){}; function C(){}; var a = new A(); B.prototype = a; var b = new B(); C.prototype = b; c = new C();
也就是c中维护者一个B的实例b的引用,而b中维护者一个A的实例a的引用。说白了这里的继承其实也就是共享对象,而且可以形成共享链。画成图如下:
(a)没有继承之前:
function A(){};
function B(){};
function C(){};
var a = new A();
var b = new B();
var c = new C();
结构如图:
(b)继承之后:
function A(){};
function B(){};
function C(){};
var a = new A();
B.prototype = a;
var b = new B();
C.prototype = b;
c = new C();
由此更加能够表示,其实所谓的继承就是对象共享,是一种链式的间接访问,比如B知道a的地址,那么B的所有子孙也就知道a的地址,自然也就能访问。但是每个B的子孙知道的是同一个a,而不是a的副本。同理C知道b(B的一个实例)的地址,那么C的所有实例也知道b的地址,而b又知道a的地址。所以c(C的一个实例)能够访问b,通过b又能访问a。如果我们把对象看成一个集合的话,那么b={....,&a},c={...,b={...,a}}; 也就是a的引用&a属于b,且包含于b;b的引用&b属于c,也包含于c。原型链的表示为:
b.__proto__ = a;c.__proto__ = b;c.__proto__.__proto__ = a;
按C/C++的角度来讲,其实所谓的对象共享就是拥有某一个对象的指针。如B继承自对象a,那么相当于B中有一个指针Pointer,那就是B.Pointer = &a;这样B就共享了a了,也就是所谓的继承,在我看来这顶多算伪继承。
本文本来想通过C/C++模拟实现该机制,结果发现并非一件易事,那么就留待以后来实现吧。
参考文献:http://www.blogjava.net/heavensay/archive/2013/10/20/405440.html(强烈推荐)