浅谈JavaScript继承

Javascript继承

学过后端语言的同学对继承并不陌生,但是对JS继承少许还是有些困惑,不要试图问我是如果知道的,其实javascript继承主要是基于原型prototype实现的。

其实当你真正了解了原型链时候,再看js继承,其实比OOP语言更灵活、更简单一些。接下来我们来看看原型链继承吧:

    //父类
    function Animal(){}
    //子类
    function Dog(){}
    //继承
    Dog.prototype = new Animal();

其实,就是把子类的prototype指向父类的实例,继承就完成了,很简单吧。这就是原型链继承
上面只是一个简单的继承结果,并无实际意义,继承的目的就是要共享父类的属性和方法,接下来我们一步一步来揭开这神秘的面纱

    /**
     *
     * 父类,带属性
     * @constructor
     * @param name 名字
     * @param type  动物分类
     * @constructor
     */
    function Animal(name,type) {
      this.name = name || 'your name';
      this.type= type || 0;
      this.coatColor= ['white','block','yellow','brown']; //引用类型
      //函数也是引用类型
      this.speak = function () {
          console.log(this.name+' speaking .');
      }
}
    }
    
    /**
     * 为父类新增一个方法
     * @returns {boolean}
     */
    Animal.prototype.say= function () {
         console.log('my name  is '+this.name);
    };
    
    /**
     * 子类
     * @constructor
     */
    function Dog(name) {
         this.name = name;
         this.foot= 4;
    }
    
    //实现继承-原型链继承 => (子类 -> 子类原型->父类) ;继承 注意,继承必须要写在子类方法定义的前面
    Dog.prototype = new Animal();
    
    /**
     * 子类方法
     *  为子类新增一个方法(在继承之后,否则会被覆盖/异常)  dog.run is not a function
     */
    Dog.prototype.run = function () {
        console.log('The '+ this.name +' was runing.');
    };
    var dog = new Dog('taiSen');
    console.log(dog.name); //dog    --子类覆盖父类的属性
    console.log(dog.type); // 0     --父类的属性
    console.log(dog.foot); //4      --子类自己的属性
    dog.say(); //my name  is taiSen    --继承自父类的方法
    dog.run(); //The taiSen was runing. --子类自己的方法

以上,看起来我们好像已经完成了一个完整的继承了。但是,原型链继承有一个缺点,就是属性如果是引用类型的话,会共享引用类型 ,接下来我个Animal增加引用类型属性this.coatColor,测试下

    //测试下
    var dog1= new Dog();
    var dog2 = new Dog();
    dog1.coatColor.push('blue');
    console.log(dog1.coatColor); // [ 'white', 'block', 'yellow', 'brown', 'blue' ]
    console.log(dog2.coatColor); // [ 'white', 'block', 'yellow', 'brown', 'blue' ]

dog1,dog2 输出的coatColor一样,说明引用类型属性会被
所有实例共享— 这就是原型链继承的缺点,那么我们如果解决这个问题呢? 接下来我们就要借用————
构造函数继承

    //子类
    function Cat() {
        Animal.call(this)  // 构造函数继承(继承属性)
    }
    
    //测试下
    var cat1= new Cat();
    var cat2 = new Cat();
    cat1.coatColor.push('red');
    console.log(cat1.coatColor); // [ 'white', 'block', 'yellow', 'brown', 'red' ]
    console.log(cat2.coatColor); // [ 'white', 'block', 'yellow', 'brown']

从结果看,我们就解决了引用类型被所有实例共享的问题了。

注意:这里跟
原型链继承有个比较明显的区别是并没有使用
prototype继承,而是在子类里面执行父类的构造函数, 相当于把父类的代码复制到子类里面执行一遍,这样做的另一个好处就是可以给父类传参。

测试代码:

    /比如:
    function Pig(name) {
        Animal.call(this,name);
    }
    var pig1= new Animal('big Pig');
    var pig2 = new Animal('small Pig');
    console.log(pig1.name); // big Pig
    console.log(pig2.name); //small Pig

看起来是不是很像java,C#语言啊,以上构造函数解决了引用类型被所有实例共享的问题。

注意: 正因为构造函数解决了解决了引用类型被所有实例共享的问题,导致了一个相对很矛盾的问题出现了,——————
函数也是
引用类型,函数也没办法共享了.也就是说,每个实例里面的函数,虽然功能一样,但是却不是同一个函数,就相当于我们每实例化一个子类,就复制了一遍的函数代码。

//父类新增this.speak函数
function Animal(name,type) {
  this.name = name || 'your name';
  this.type= type || 0;
  this.coatColor= ['white','block','yellow','brown']; //引用类型
    //函数也是引用类型
  this.speak = function () {
      console.log(this.name+' speaking .');
  }
}

//测试
console.log(pig1.speak===pig2.speak); // false 

以上证明,父类的函数,在子类的实例下是不共享的。

怎么办呢?,以上可以看出原型链继承构造函数继承 这两种继承方式的优缺点刚好是互相矛盾的,那么我们有没有办法鱼和熊掌兼得呢? 答案是肯定的————组合继承

    // 父类
    function Animal() {
        this.name = name || 'your name';
        this.type= type || 0;
        this.coatColor= ['white','block','yellow','brown']; //引用类型
    }
    
    // 父类函数
    Animal.prototype.speak =function () {
          console.log(this.name+' speaking .');
      }
    
    // 子类
    function Chicken(){
        Animal.call(this)             // 构造函数继承(继承属性)
    }
    // 继承
    Chicken.prototype = new Animal()  // 原型链继承(继承方法)

总结

继承方式核心代码优缺点用法
原型链继承Dog.prototype = new Animal()实例的引用类型共享继承属性
构造函数继承在子类Cat)里执行 Animal.call(this)会独享所有属性,包括引用属性(重点是函数)继承方法
组合继承利用原型链继承要共享的属性,利用构造函数继承要独享的属性实现相对完美的继承结合上两位

本文中的代码见demo coding ,如果觉得对您有用,帮加个star,万分感谢!

今天就写到这,讲述了3种继承方式,其实J继承还有很多继承方式。其他留在下期再见咯。
感觉各位的收看,欢迎提问。

    原文作者:hankblog
    原文地址: https://segmentfault.com/a/1190000014945479
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞