• 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控件的经验分享出来,希望对大家有帮助。

  • 相关阅读:
    定时器的应用---查询方式---让8个LED灯,左右各4个来回亮
    单片机实现60s定时器
    单片机不同晶振怎么计算延迟时间?
    573锁存器驱动8段数码管
    51单片机英文引脚等中文对照
    Java【小考】
    viso2010从mysql中导出ER图
    驱动继电器实验
    驱动蜂鸣器的实验
    驱动数码管的实验
  • 原文地址:https://www.cnblogs.com/zaohe/p/2723148.html
Copyright © 2020-2023  润新知