JS实现继承的七种方法

Class + extend

Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}

想了解 Class + extend可以前往ES6——Class 笔记ES6——Class 的继承 笔记

原型链
function Parent(){ 
    this.age = 23; 
}
Parent.prototype.getAge = function(){ 
    return this.age; 
}; 
function Child(){ 
    this.name = "xd"; 
} 

//继承了 Parent 
Child.prototype = new Parent(); 
Child.prototype.getName = function(){ 
    return this.name;
}; 
   
var child = new Child(); 
child.getAge() //23

问题:

  1. 包含引用类型值的原型属性会被所有实例共享。下列代码可以用来说明这个问题:
    function Parent(){ 
        this.friend = ['jh','zh']; 
    }
    function Child(){} 
    
    //继承了 Parent
    Child.prototype = new Parent(); 
    
    var child1 = new Child();
    child1.friend.push('zf');
    
    var child2 = new Child();
    child2.friend   //["jh", "zh", "zf"]
    
  2. 在创建子类型的实例时,不能向超类型的构造函数中传递参数。
借用构造函数

借用构造函数可以解决包含引用类型值的原型属性会被所有实例共享这个问题。

function Parent(){ 
    this.friend = ['jh','zh']; 
}
function Child(){
    //继承了 Parent
    Parent.call(this);
} 

var child1 = new Child();
child1.friend.push('zf');

var child2 = new Child();
child2.friend   //["jh", "zh"]

相对于原型链而言,借用构造函数有一个很大的优势,即可以在子类型构造函数中向超类型构造函数传递参数。

function Parent(age){ 
    this.age = age; 
}
function Child(){
    //继承了 Parent,同时还传递了参数
    Parent.call(this,23);

    //实例属性
    this.name = 'xd';
} 

var child = new Child();
child.age //23
child.name //'xd'

问题:方法都在构造函数中定义,因此函数复用就无从谈起了。

组合继承(常用)

组合继承的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。

function Parent(age){ 
    this.age = age; 
    this.friend = ['jh','zh'];
}
Parent.prototype.getAge = function(){
    return this.age;
}
function Child(age, name){
    //继承了属性
    Parent.call(this, age);

    this.name = name;
} 
Child.prototype = new Parent();
Child.prototype.constructor = Child;
Child.prototype.getName = function(){
    return this.name;
}

var child1 = new Child(23, 'xd');
child1.friend.push('zf');
child1.age //23
child1.name //'xd'
child1.getName()    //'xd'
child1.getage()    //'23

var child2 = new Child(18, 'ah');
child2.friend   //['jh','zh']
原型式继承

原型式继承的思想就是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。

var person1 = { 
    name: "xd", 
    friends: ["jh", "zh"] 
}; 

var person2 = Object.create(person1); 
person2.name = "hc"; 
person2.friends.push("zf"); 

var person3 = Object.create(person1); 
person3.name = "xs"; 
person3.friends.push("ah"); 

person1.friends  //["jh", "zh", "zf", "ah"] 

在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式继承是完全可以胜任的。

问题:包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样。

寄生式继承

寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。

var createPerson = function(parent){
    var child = Object.create(parent);  //通过调用函数创建一个新对象
    child.sayHi = function(){   //以某种方式来增强这个对象
        console.log('Hi!');
    }
    return child;   //返回这个对象
}

var person = {
    name: 'xd',
    friend: ['jh', 'zh']
};
var person2 = createPerson(person);
person2.sayHi();    //'Hi!'

问题:使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一点与构造函数模式类似。

寄生组合式继承

所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。

function inheritPrototype(Child, Parent){ 
    var parent = Object.create(Parent.prototype);    //创建对象
    parent.constructor = Child;    //增强对象
    Child.prototype = parent;  //指定对象
}
function Parent(age){
    this.age = age;
    this.friend = ['jh','zh'];
}
Parent.prototype.getAge = function(){
    return this.age;
}
function Child(age, name){
    Parent.call(this, age);
    this.name = name;
}
inheritPrototype(Child, Parent);
Child.prototype.getName = function(){
    return this.name;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值