Js 控件编写 继承:extend、mixin和plugin(一)

Js 继承:extend、mixin和plugin(一)

 

简介:

我们编写前端控件时,需要给控件建立一个体系,面向对象是一个很合适的方式,但是JS本身对面向对象的一些概念支持偏弱,特别是继承的特性方面,那么我们就必须通过一系列的方式来实现继承。

 

Extend方式:

Extend方式非常贴近面向对象语言中的类继承,这种方式使用原型链的方式来实现继承。原型链的继承方式有几个缺点:

1)缺少针对父类的引用,例如:

 

function A(){

//初始化A操作

}

A.prototype = {

method1:function(){

//A的一系列操作

}

}

function B(){

//初始化B的操作

}

B.prototype = new A();

B.method1 = function(){

//先执行 A.method1的方法

//执行自己的方法

}

 

那么此时如何在调用A的method1方法呢?我们没有java中的super对象也缺少C#中的base对象,当然我们可以换一种写法,来解决这个问题:

 

var  aobj = new A(); 

B.prototype = aobj;

B.method1 = function(){

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;

return subclass;

}

 

上面的函数解决了superclass的问题但是又引入了新的问题,下面我们一一来讲。

2)破坏了对象的constructor属性,这个属性在使用继承的过程中非常有用,它指向的是对象的构造函数,例如:

 

function A(){}

//未继承 A时

function B(){}

var b = new B();

alert(b.constructor === B) //true

 

//B继承A 

extend(B,A); 

b1 = new B(); 

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;

  return subclass;

}

 

到这里看似解决了继承链的问题,但是此时我们来看一下下面情形:

 

function C (){} 

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();

  return subclass;

}

 

此时我们再来调用一下:

 

alert(c.constructor.superclass.constructor);//B

 

此时的还有什么问题呢?我们来做一下实验:

 

function A (config){

  console.log('a constructor,config:' + config);

}

A.prototype = {

  method : function(){

    console.log('a runing');

  }

};

function B (config){

  this.constructor.superclass.constructor.call(this,config);

  console.log('b constructor,config:' + config);

}

extend(B,A);

function C (config){

  this.constructor.superclass.constructor.call(this,config);

  console.log('c constructor,config:' + config);

}

extend(C,B);

var c = new C('hello');

c.method();

 

输出结果:

 

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即可:

 

function B (config){

  B.superclass.constructor.call(this,config);

  console.log('b constructor,config:' + config);

}

function C (config){

  C.superclass.constructor.call(this,config);

  console.log('c constructor,config:' + config);

}

 

针对调用extend时生成多个实例我们引入方法:

 

function create(proto, c) {

    function F() {

    }

    F.prototype = proto;

    var o = new F();

    o.constructor = c;

    return o;

}

 

在extend方法中我们这样调用:

 

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) {

        function F() {

        }

        F.prototype = proto;

        var o = new F();

        o.constructor = c;

        return o;

    }

  var superObj = create(superclass.prototype,subclass);

    subclass.prototype = superObj;

    subclass.superclass = create(superclass.prototype,superclass); 

  return subclass;

}

 

这样在执行上面的代码即得到:

 

a constructor,config:hello

b constructor,config:hello

c constructor,config:hello

a runing 

 

今天先写到这里,接下来我会把控件的继承机制一下,如果有时间,把一些编写js控件的经验分享出来,希望对大家有帮助。

posted @ 2012-10-13 23:34  zaohe  阅读(2476)  评论(2编辑  收藏  举报