JavaScript实现继承的几种方式

JavaScript实现继承的几种方式

  • 原型链继承

    function Father(){
        this.names = ['a','b'];
    }
    Father.prototype.getName = function(){
        console.log(this.names);
    }
    function Child(){}
    Child.prototype = new Father();
    
    //创建实例
    var c1 = new Child();
    var c2 = new Child();
    
    c1.getName(); //['a','b']
    c2.getName(); //['a','b']
    c1.names = ['a'];
    c1.getName(); //['a']
    c2.getName(); //['a']
    

    缺点:

    • 使用原型链继承实现子类继承父类,会存在引用类型被所有实例共享的情况,改变其中一个实例中引用类型的值,其他实例中的该属性也会改变。

    • 需要为子类添加属性和方法时,必须在new Child语句之后执行,不能放在构造器中

    • 创建子实例时,无法向父类构造函数传参

    • 无法实现多继承

  • 借用构造函数

    function Father(){
        this.names = ['a','b'];
    }
    function Child(){
        Father.call(this);
    }
    
    var c1 = new Child();
    var c2 = new Child();
    console.log(c1.names); //['a','b']
    console.log(c2.names); //['a','b']
    c1.names = ['a'];
    console.log(c1.names); //['a']
    console.log(c2.names); //['a','b']
    

    避免了原型链继承的应用类型共享问题;创建子实例时,可以向父类构造函数传参;可以实现多继承(call多个父类对象)

    缺点:

    • 只能继承父类的属性和方法,无法继承原型属性和方法
    • 每个子例都有父类实例函数的副本,无法实现函数复用
  • 组合继承

    原型链实现原型属性和方法的继承,借用构造函数实现实例属性的继承

    function Parent (name) {
        this.name = name;
        this.colors = ['red', 'blue', 'green'];
    }
    
    Parent.prototype.getName = function () {
        console.log(this.name)
    }
    function Child (name, age) {
    
        Parent.call(this, name);
        
        this.age = age;
    
    }
    Child.prototype = new Parent();
    
    var c1 = new Child('张三', '18');
    
    c1.colors.push('black');
    
    console.log(c1.name); // 张三
    console.log(c1.age); // 18
    console.log(c1.colors); // ["red", "blue", "green", "black"]
    
    var c2 = new Child('李四', '20');
    
    console.log(c2.name); // 李四
    console.log(c2.age); // 20
    console.log(c2.colors); // ["red", "blue", "green"]
    

    存在和原型链继承一样的问题:所有实例共享一个引用类型

  • 寄生组合继承

    寄生组合式继承强化的部分就是在组合继承的基础上减少一次多余的调用父类的构造函数:

    function create(obj) {
      function F() {};
      F.prototype = obj;
      return new F();
    }
    
    function Animal(color) {
      this.color = color;
      this.name = 'animal';
      this.type = ['pig', 'cat'];
    }
    Animal.prototype.greet = function(sound) {
      console.log(sound);
    }
    function Dog(color) {
      Animal.apply(this, arguments);
      this.name = 'dog';
    }
    /* 注意下面两行 */
    Dog.prototype = Object.create(Animal.prototype);
    Dog.prototype.constructor = Dog;
    
    Dog.prototype.getName = function() {
      console.log(this.name);
    }
    
    var dog = new Dog('白色');   
    var dog2 = new Dog('黑色');     
    
    dog.type.push('dog');   
    console.log(dog.color);   // "白色"
    console.log(dog.type);   // ["pig", "cat", "dog"]
    console.log(dog2.type);  // ["pig", "cat"]
    console.log(dog2.color);  // "黑色"
    dog.greet('汪汪');  //  "汪汪"
    

    将父类原型上的方法拷贝后赋给Dog.prototype,这样子类上就能拥有了父类的共有方法,而且少了一次调用父类的构造函数,并且因此避免了在 Animal.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。

  • extends继承

    Class和extends是在ES6中新增的,Class用来创建一个类,extends用来实现继承:

    class Animal {   
      constructor(color) {   
        this.color = color;   
      }   
      greet(sound) {   
        console.log(sound);   
      }  
    }   
    
    class Dog extends Animal {   
      constructor(color) {   
        super(color);   
        this.color = color;   
      }  
    }   
    
    let dog = new Dog('黑色');  
    dog.greet('汪汪');  // "汪汪"
    console.log(dog.color); // "黑色"
    

    注意:子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值