js 5种继承实现方式

276 阅读3分钟

js5种继承方式

  • 原型链
  • 构造函数
  • 组合式
  • 组合寄生式
  • ES6提供的class extents

从前到后,缺点越来越少,越来越优化

一、原型链继承

实现方式

将子类原型设置为父类对象的实例

优点:

  • 简单、易实现

缺点:

  • 全为静态属性,所有父类相关属性都在原型上,某个实例对象无法创建在父类中定义的自己专属的属性
  • 创建子类对象时,无法向父类构造函数传参,因为一开始就确定了原型
  • 一旦某个子类实例修改了静态属性,则所有实例均被修改,因为共享同一个原型

总结

js继承中最简单的一种实现方式,但是有缺陷。

function Person(name){
    this.name = name;
}
Person.prototype.hasname=function(){
    console.log("My name is ", this.name)
}
function Teacher(){};
Teacher.prototype=new Person()
Teacher.prototype.constructor = Teacher;

二、构造函数继承

实现方式:

在子类构造函数中调用一次父类构造函数

优点:

  • 可向父类传参
  • 某个实例修改属性不会影响其他实例

缺点:

  • 无静态属性,父类构造函数原型上的内容无法继承
  • 属性或方法,想要被继承,只能在父类构造函数中定义
  • 每创建一个新实例对象,对应继承属性都会被创建一次

总结

解决了原型链继承存在的问题,但带来了新的问题。

function Person(name){
    this.name = name;
}
function Teacher(book, name){
    Person.call(this, Array.prototype.slice.call(arguments, 1))
    this.book=book
};

三、组合继承

实现方式

将构造函数继承和原型链继承组合起来

优点

既解决了原型链继承全为静态属性的问题,又解决了构造函数继承无静态属性的问题

缺点

产生了副作用,各个实例对象上的属性虽然相互独立,但其原型链上还有额外同名属性

总结

组合继承的本质上是组合使用构造函数继承和原型链继承,将原型链继承中的静态属性或方法通过构造函数继承重写一遍,因此会造成对象属性和原型链上静态属性重复

function Person(name){
    this.name = name;
}
Person.prototype.hasname=function(){
    console.log("My name is ", this.name)
}
function Teacher(book,name){
    Person.call(this, Array.prototype.slice.call(arguments, 1))
    this.book=book
};
Teacher.prototype=new Person()
Teacher.prototype.constructor = Teacher;

四、组合寄生式继承

实现方式

在组合继承基础上,将子类原型指向由原本的父类实例,变为了父类原型的复制对象,采用复制对象是为了避免父类实例操作影响子类实例

优点

解决了上述所有问题

缺点

书写复杂

总结

组合寄生式继承解决了前三种继承方式的问题,是ES5及以前旧版标准下较为完美的继承方案

function Person(name){
    this.name = name;
}
Person.prototype.hasname=function(){
    console.log("My name is ", this.name)
}
function Teacher(book,name){
    Person.call(this, Array.prototype.slice.call(arguments, 1))
    this.book=book
};
Teacher.prototype = Object.creat(Person.prototype)
Teacher.prototype.constructor = Teacher;

五、ES6 class继承方案

实现方式

class是ES6提供的语法糖,本质上就是组合寄生式继承。相比于组合寄生式继承,多了对键的处理,详见后续代码。是ES6+下的更完美的继承方案

优点

  • 拥有组合寄生式继承继承的优点
  • 书写清晰易懂
class Person{
    constructor (name){
        this.name = name;
    }

    function hasname(){
        console.log("My name is ", this.name)
    }
}

class Teacher extends Person{
    constructor (book,name){
        super(name)
        this.book = book;
    }
}

// 内部实现相比组合寄生式继承额外多做了如下操作
// for(let k in Person){
//     if(Person.hasOwnProperty(k) && !(k in Teacher)){
//         Teacher[k] = Person[k]
//     }
// }