Js 继承:extend、mixin和plugin(一)
简介:
我们编写前端控件时,需要给控件建立一个体系,面向对象是一个很合适的方式,但是JS本身对面向对象的一些概念支持偏弱,特别是继承的特性方面,那么我们就必须通过一系列的方式来实现继承。
Extend方式:
Extend方式非常贴近面向对象语言中的类继承,这种方式使用原型链的方式来实现继承。原型链的继承方式有几个缺点:
1)缺少针对父类的引用,例如:
那么此时如何在调用A的method1方法呢?我们没有java中的’super’对象也缺少C#中的’base’对象,当然我们可以换一种写法,来解决这个问题:
aobj.method1.call(this);//先执行 A.method1的方法
但是我们必须能缓存刚才的 aobj对象,方便在调用B.method1时取得到,所以我们就引入’superclass’字段,放在构造函数B上作为静态属性,调用时:B.superclass.method1.call(this),下面是实现:
function extend(subclass,superclass){
var superObj = new superclass();
subclass.prototype = superObj;
subclass.superclass = superObj;
上面的函数解决了superclass的问题但是又引入了新的问题,下面我们一一来讲。
2)破坏了对象的constructor属性,这个属性在使用继承的过程中非常有用,它指向的是对象的构造函数,例如:
alert(b.constructor === B) //true
//B继承A
alert(b1.constructor === B) // false
alert(b1.constructor === b.constructor) //false
这样的结果绝对不是我们需要的,否则我们在编写方法的过程中调用,B.supercalss.constructor.call(this) 或者 B.superclass.constructor.superclass 会出现混乱,我们怎么办呢,继续改进extend方法,矫正constructor 属性:
function extend(subclass,superclass){
var superObj = new superclass();
subclass.prototype = superObj;
superObj.constructor = subclass;//矫正 constructor属性
subclass.superclass = superObj;
到这里看似解决了继承链的问题,但是此时我们来看一下下面情形:
extend(C,B);
var c = new C();
alert(c.constructor.superclass.constructor);//C
上面的结果意外的不是B 而是 C,这是什么原因呢,我们看刚才矫正constructor 属性以及上面一句:
subclass.prototype = superObj;
superObj.constructor = subclass;
我们将C的prototype的constructor修改为了C ,由于subclass.superclass = superObj;那么C的superclass跟C 的prototype是同一个对象,那么 c.constructor.superclass.constructor等同于C.prototype.constructor 结果就是 C,那么我们需要做一下修改:
function extend(subclass,superclass){
var superObj = new superclass();
subclass.prototype = superObj;
superObj.constructor = subclass;//矫正 constructor属性
subclass.superclass = new superclass();
alert(c.constructor.superclass.constructor);//B
console.log('a constructor,config:' + config);
this.constructor.superclass.constructor.call(this,config);
console.log('b constructor,config:' + config);
this.constructor.superclass.constructor.call(this,config);
console.log('c constructor,config:' + config);
输出结果:
a constructor,config:undefined
a constructor,config:undefined
b constructor,config:undefined
b constructor,config:undefined
b constructor,config:undefined
Uncaught RangeError: Maximum call stack size exceeded
我们看到没有输出我们希望的结果,这里有2个问题,
1. 产生了循环调用
2. 未实例化对象前,实例化了多个父类对象,而且这些实例初始化时传入的参数为空,极易出现错误。
解决死循环我们只需要把this.constructor.superclass 替换为 对应的 B.superclass即可:
B.superclass.constructor.call(this,config);
console.log('b constructor,config:' + config);
C.superclass.constructor.call(this,config);
console.log('c constructor,config:' + config);
function create(proto, c) {
var superObj = create(superclass.prototype,subclass);
subclass.prototype = superObj;
subclass.superclass = create(superclass.prototype,superclass);
此时我们的prototype 属性和superclass属性都不需要实例化对象,完整的extend方法如下:
function extend(subclass,superclass){
function create(proto, c) {
var superObj = create(superclass.prototype,subclass);
subclass.prototype = superObj;
subclass.superclass = create(superclass.prototype,superclass);
a constructor,config:hello
b constructor,config:hello
c constructor,config:hello
今天先写到这里,接下来我会把控件的继承机制一下,如果有时间,把一些编写js控件的经验分享出来,希望对大家有帮助。