简化的 Ext 原型链的类设计
function A() { alert('a'); } function B() { B.superclass.constructor.call(this); } function temp() { } temp.prototype = A.prototype; //防止A 的实例属性也搞到 B 里去 ,没有意思,我们只需要 A 的 prototype 里的公共属性 //不能使用 B.prototype=A.prototype 这样的话修改 B.prototype 则 A.prototype 也变了,以及影响instanceof //不建议 B.prototype =new A() ;这样的话 A 的实例方法也到 B.prototype 里去了 B.prototype = new temp(); //上一步后 B 的实例的构造函数属性为 A 了, // B.prototype.constructor == A //为了以后方便B的子类调用B的构造函数 //如 C.superclass.constructor.call(this); //要纠正 //B 的实例 的 构造函数属性要设为 B B.prototype.constructor = B; //要记录 B 的父亲,便于以后调用父亲方法 B.superclass = A.prototype; if (A.prototype.constructor == Object.prototype.constructor) { A.prototype.constructor = A; }
简要分析:
1.temp 作为一个空函数,将用它创建的一个对象实例插入到原型链中。这样做可以避免创建超类的新实例,因为他可能比较庞大,而且有时超类的构造函数有一些副作用(超类实例的数据属性到子类的原型中),或者会执行一些需要大量计算的任务。
2.最后三行代码则用来确保超类的 prototype 的 constructor 属性已经被正确设置(即使超类就是Object类本身),在用到这个新的superclass属性调用超类的构造函数时这个问题很重要。
3.最后一行是为了如果父类的 constructor 被重置了,这时候就要设回来, 我怀疑是不是 extjs 担心 这样定义类
function x() {} x.prototype = { y: function() {} };
这样的话 x.prototype.constructor == Object.prototype.constructor 了 ,继承 x 时需要重置 x.prototype.constructor = x, 这样才能子类 sub 继承 x 时, sub.superclass.constructor == x ,但是正确的定义应该是
x.prototype.y=function(){}
他是为了预防第一种定义原型方法吧 ,我一直觉得整个设置
x.prototype={};
是不好的,它把constructor也覆盖了 ,应该是 x.prototype.y=....或者一些工具函数比如 ext的override 等等 。
总之上述代码完美实现了一个类原型继承链,并且可以有子类显式调用父类构造函数了。
PS: instanceof 问题
instanceof 表面来看是来判断是否对象是某个函数new出来的,但实际上只是在原型链的比较操作:
看个例子:
function p(){} function c(){} c.prototype=p.prototype; function d(){} d.prototype=p.prototype; var a=new d(); alert(a instanceof c);
意外么?所以我们不建议直接
function p(){} function c(){} c.prototype=p.prototype;
ejohn 在一篇文章 中提到的 instanceof 机制模拟
function instanceOf(object, constructor) { while (object != null) { if (object == constructor.prototype) return true; object = Object.getPrototypeOf(object); } return false; } function p(){} function c(){} c.prototype=p.prototype; function d(){} d.prototype=p.prototype; var a=new d(); alert(instanceOf(a,c))
现在明白了吧。其中 getPrototypeOf 为ecmascript标准中的获得对象内部 _proto_ 属性,指向它的构造函数的 prototype.(IE没有实现哦)。
ps2 : 实例属性问题
function xy(){ } xy.prototype.z={q:1}; var x=new xy(); alert(x.z.q); //可以吧这句代码注释掉,看看前后结果 x.z={q:3}; alert(xy.prototype.z.q); x.z.q=2; alert(xy.prototype.z.q);
简要分析:
1.对象读取属性时,(若该属性对象本身没有) 会向类的prototype相应属性查看读取。
2.对象设置属性时,会直接修改自身的属性,和类的prototype相应属性无关。
3.x.z.q=1 读分析 :x.z 读取的是 ,(若z不存在x本身属性), 类的prototype相应属性 ,x.z.q 则设置了原型z属性的 q值。
4.x.z={q:3} 写分析: 若直接设置 x.z 则设置的是 x对象本身的属性 ,和 类的prototype相应属性z无关 。
总之是:由于原型链机制而导致的读和写的不对等性造成的。