zoukankan      html  css  js  c++  java
  • javascript继承之学习笔记

    今天记录一下学习javascript的继承。

    继承基本上是基于“类”来说的,而javascript中并不存在真正的类,所以就出现了各种模拟“类”的行为,然后就堂而皇之的使用起了类的概念。这里不谈“类”的概念对js学习造成了多大的困难,只说一下普通意义上继承的6种方式。

    1、原型链继承

    这是js开发人员一开始接触js,最容易学会的,所以说他就是最简单的继承的方法:

    function Super(val) {
       this.val = val;
       this.arr = [1];
     }
     Super.prototype.fn = function () {
       console.log(this.arr)
     }
     
     function Subtype(val, name) {}
     Subtype.prototype = new Super();
     var b1 = new Subtype(12);
     console.log(b1.val)				// undefined
     var b2 = new Subtype();
     b1.fn();        			// [1]   
     b1.arr.push(2);     
    console.log(b2.arr);        //  [1,2]

    很容易的构造了一个子类Subtype,继承了父类的所有属性和共享方法。

      但是缺点也很明显:

        1、子类无法传入初始参数,对于这里而言,就是Supertype无法为内部属性val传递一个合理的值;b1实例化的时候,为val传入参数12,但是b1.val却取不到该值。

        2、如果属性值不是一个标量基本类型,是一个对象,则会出现如果子类实例对该对象进行修改,由于对象属性所存储的仅仅是一个引用,而不是其真实值,所以会导致修改一个实例的对象属性的值,导致另一个实例的该对象引用的值跟着变化。这里就是对实例b1的属性arr添加值操作的话,实例2引用的arr会同事变化,因为本质上他们所指向的是一个值。

    为了修改上述问题,则引申出了第二种继承方式:

    2、借用构造函数

         function Super(val) {
            this.val = val;
            this.arr = [1];
            this.fn = function () {
              console.log('prototype');
            }
          }
          
          function Subtype() {
            Super.apply(this, arguments);
          }
          var sub1 = new Subtype('s', 'zhongg');
          var sub2 = new Subtype('sdsdds', 'fsdajfsldaf');
          sub1.arr.push(5);
          console.log(sub2.arr);
    

      很明显,修改了上述原型链继承方式的两个存在问题,但是却又产生了一个新的问题,函数不能公用的问题,由于子类是直接调用的父类函数,这就与原型prototype没有任何关系了,从而会导致定义在原型prototype上的任何函数都不起作用,这么一来,所有需要用到的函数就必须定义在父类函数内部。当子类比较多,实例化很多的时候,这就会造成每一个实例化都实例化了相同的函数,会占用大量内存。

      那么必然的,既然目前的解决方案还不完美,肯定会有更完美的解决方案:

    3、组合继承

          function Super(val) {
            this.val = val;
            this.arr = [1];
          }
          Super.prototype.fn = function () {
            console.log(this.val + 'prototype');
          }
          function Subtype(val, name) {
            Super.apply(this, arguments);
          }
          Subtype.prototype = new Super();
          var sub1 = new Subtype('值', '键值对');
          var sub2 = new Subtype('值2', '键值对2');
          sub1.fn();
    

      组合继承,顾名思义,就是组合了其他的继承方式而成,这里其实就是组合了原型链继承和借用构造函数继承。

      已经基本上完美了,综合了前两种继承方式的所有优点。既然说是基本上完美,那肯定还是有点瑕疵的,这里的瑕疵就是调用了两次父类函数,一次直接调用,一次new调用,导致生成了两份的实例属性,对于内存而言也是一种浪费。

      到ES5时候,出现了一种可以真正完美解决继承所有缺点的继承方式:

    4、寄生组合继承  

         function Super(val) {
            this.val = val;
            this.arr = [1];
          }
          Super.prototype.fn = function () {
            console.log(10 + this.val);
          }
          function Subtype(val, name) {
            Super.apply(this, arguments);
            this.name = name;
          }
          Subtype.prototype = Object.create(Super.prototype);
          Subtype.prototype.fun = function () {
            console.log("fnu:" + this.name);
          } 
          Subtype.prototype.constructor = Subtype;
          var sub1 = new Subtype('zhi', '名字是什么比较好呢?');
          var sub2 = new Subtype('again', 'nameto');
          console.log(sub1.constructor)
          sub1.fun();
          sub1.fn();
          sub1.arr.push(3);    
    

      利用的是ES5当中的Object.create(...),《你不知道的javascript》中对Object.create(..)的解释:Object.create(..)会创建一个新对象并把他关联到我们指定的对象。这里就是创建一个新的对象 Subtype.prototype 并把他关联到 Super.prototype,这里也会产生另外一个小问题,需要进行修补。就是Object.create(..)创建的对象没有constructor属性,如果需要constructor属性的话,那么在Subtype.prototype创建之后,需要手动修补,Subtype.prototype.constructor = Subtype。这么一来,继承上产生的各种问题都真正完美了。

      由于Object.create(...)是在ES5中定义的,所以这个方案提出之前,其实利用的是Object.create(...)的polyfill的方案,类似于:  

        if (!Object.create) {
            Object.create = function (o) {
              function F (){};
              F.prototype = o;
              return new F();
            }
          }
    

       但是由于该方案出现的时间比较晚,虽然此方案是真正完美的方案,但是并没有上述的组合继承的方案使用广泛。事实上,组合继承的方案也基本上能够保证我们的日常开发了。

    另外还有两种继承方案,可是已经不太像继承了:

    5、原型式继承

          function Super(val) {
            this.val = val;
            this.arr = [1];
          }
          Super.prototype.fn = function () {
            console.log(10 + this.val);
          }
          var sup = new Super('父类');
          var sub = Object.create(sup);
          sub.func1= function(){}; 
        // 后面就是为子类sub添加各种需要的方法或者属性

      仔细比较一下,其实原型式继承和原型链继承还是比较相似的,区别在于原型链继承的 Subtype.prototype = new Super();而原型式继承为 var sup = new Super('父类'); var sub = Object.create(sup);应该能够看出区别所在了。

    6、寄生继承

    最后一种继承方式真心的不是很想说,没什么可说的,其实就是把原型式继承的子类继承部分封装成函数来实现而已。

         function Super(val) {
            this.val = val;
            this.arr = [1];
          }
          Super.prototype.fn = function () {
            console.log(10 + this.val);
          }
          function Subtype(o){
            var clone = Object.create(o);
            clone.func1= function(){};
            //  添加各种属性
          }
          var sub = Subtype(new Super());    
    

      在日常开发中,一般使用第三种组合继承的方式,如果想要求更高一点的话,可以使用第四种寄生组合继承的方式。

      

      

              

  • 相关阅读:
    Week-1 linux命令行重点整理
    Week-1 linux命令行重点整理
    k8s namespace限制调研
    k8s namespace限制调研
    CSS开发技巧(三):图片点击缩放
    Failed building wheel for cytoolz
    Failed building wheel for cytoolz
    web.config访问走代理的配置
    bzoj1799(洛谷4127)同类分布(月之谜)
    bzoj4247挂饰——压缩的动态规划
  • 原文地址:https://www.cnblogs.com/zhuhuoxingguang/p/6339734.html
Copyright © 2011-2022 走看看