看了第三章前半部分extend作者实现的superclass() 部分,不知道为何在分析extjs源码中途要创造这样一个继承机制,看起来像 crockford 的uber 方法,但是觉得有点问题啊,先记下来
作者实现:
extend : function() { // inline overrides var io = function(o) { for (var m in o) { this[m] = o[m]; } }; var oc = Object.prototype.constructor; var superclass = function(sp) { var s = function() { var name = (s.caller || {}).name; var len = arguments.length, t = this; var supper = arguments.callee.superclass; if (!name) for (var n in t) { if (t[n] == s.caller) { name = n; break; } } if (len > 0 && name) { //alert(name); var callArgs = Array.prototype.slice.call( arguments, 0); Array.prototype.splice.apply(callArgs, [0, 1]); return supper[name](callArgs); } return supper; } s.superclass = sp; return s; }; return function(sb, sp, overrides) { if (typeof sp == 'object') { overrides = sp; sp = sb; sb = overrides.constructor != oc ? overrides.constructor : function() { sp.apply(this, arguments); }; } var F = function() { }, sbp, spp = sp.prototype; F.prototype = spp; sbp = sb.prototype = new F(); sbp.constructor = sb; sb.superclass = spp; if (spp.constructor == oc) { spp.constructor = sp; } sb.override = function(o) { Ext.override(sb, o); }; sbp.override = io; Ext.override(sb, overrides); sb.extend = function(o) { Ext.extend(sb, o); }; sbp.superclass = superclass(spp); return sb; }; }(),
this.superclass() 实际上直接就访问到了父类构造函数的原型对象,那么以此为原理可以直接调用父类的方法,作者也说了这样的话对象实例(this)就丢掉了,到这还好。
例子: (下载)
不过我不大清楚这样子脱离了实例this的上下文调用还有什么用,就开始看例子,例子就很随意了:(稍微改了一下)
var Animal = function(q) { //alert("this is Animal constructor"); this.name = q[0][0].tag; this.age = q[0][0].age; }; Animal.prototype.getAge = function() { alert(this.name+"'age is "+this.age); }; Animal.prototype.getName = function() { // alert("this is the animal Name!"); }; var Cat = Ext.extend(Animal, { name : 'cat', age : 5, yy : 'aa', constructor : function() { //alert("this is Cat constructor"); this.superclass().constructor(arguments); }, getAge : function() { //alert(this.name + "'age is " + this.age); this.superclass().getAge(); this.name = 'modefy'; this.xx = 'aa'; //alert(this.yy); } }); var homeCat = function() { //alert("this is homeCat constructor"); this.superclass().constructor(arguments); }; Ext.extend(homeCat, Cat, { name : 'homeCat', age : 3, homeCat : true, getAge : function() { // alert(this.name+"'age is "+this.age); this.yy = 'yy'; this.superclass(this);// .getAge(); // alert(this.name); // alert(this.xx); } }); var myCat = new homeCat({age:99,tag:"first"}); myCat.getAge(); //console.dir(Animal.prototype); var myCat2 = new homeCat({age:-99,tag:"second"}); myCat2.getAge(); myCat.getAge();
调用代码:
var myCat = new homeCat({age:99,tag:"first"}); myCat.getAge(); //console.dir(Animal.prototype); var myCat2 = new homeCat({age:-99,tag:"second"}); myCat2.getAge(); myCat.getAge();
输出为:
first'age : 99
second'age -99
second'age -99
可见前面对象的父类数据已经被覆盖掉了.没有达到模拟传统面向对象语言继承的机制
分析:
1.this.superclass().constructor(arguments) ,这应该是完全不合道理的 ,要么用call,要么apply,直接传个arguments似乎不妥。
2.有上述可知:
this.superclass()为父类构造函数的原型,那么在原型调用函数 this.super().xx();那么在xx中的this就变成了当前函数原型对象,那么我如果修改了this的某个数据属性话,该原型所关联的所有对象实际上都发生了变化,并且完全改变了下面代码的语义:
var Animal = function(q) { alert("this is Animal constructor"); this.name = 'animal'; this.age = q; };
用this修改的是函数的原型对象,而不是实例对象本身了!
比如上述我传了个
var myCat = new homeCat(99); myCat.getAge();
那么执行后在firebug中查看
console.dir(Animal.prototype);
可见并应该与实例关联的数据却被关联到了公有的父类原型对象上面,作者想模拟传统面向对象的实例对象的父类数据部分是失败的,js中父类原型对象是公用的, 那么这样还有什么意义,生成多个对象的话,其父类的数据并不能保存下来,都被后来对象冲掉了。
3.总之:我觉得原型对象还是只用来保存函数以及一些所有实例对象都不变的数据比较好,其他对象关联数据就不要保存在里面了。
ps:书中出现这样一段:
this.superclass().getXY.apply(this,arguments)
更令我疑惑了,apply后获得了实例的this上下文,那么在父类 getXY 中再调用
this.superclass().getXY.apply(this,arguments)
岂不会造成死循环? this.superclass永远都是同一个new出来的实例对象的superclass而不会调用原型链往上的对应superclass