JavaScript实现继承的7种方式总结

 更新时间:2023年04月03日 10:48:00   作者:布鲁斯要蓝调  
用官方点的话讲继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,同时还可以在子类中重新定义以及追加属性和方法。本文整理了JavaScript实现继承的7种方式,需要的可以了解一下

什么是继承

用官方点的话讲继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,同时还可以在子类中重新定义以及追加属性和方法。通俗的来说继承就是子类可以从父类那里复用一些属性和方法,从而达到复用代码的一个效果。

实现继承的6种方式

  • 原型链继承
  • 构造函数继承
  • 组合继承(伪经典继承)
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承
  • class 类继承

1. 原型链继承

function Person() {
    this.name = 'bruce'
    this.age = 18
    this.gender = '男'
    this.like = {
        sport: 'running',
        book: 'JavaScript'
    }
    this.getName = function() {
        console.log(this.name);
    }
}

function Student() {
    this.school = '新华小学'
}

let s = new Student()
console.log(s.name);   // 未继承前 undefined

Student.prototype = new Person()  // 继承之后
let s1 = new Student()
s1.getName()  // bruce
console.log(s1.like.book);  // JavaScript
s1.like.book = 'Java'   // 修改引用类型的值
s1.name = 'jack'  // 修改非引用类型的值
console.log(s1.like.book);  // Java
s1.getName()  // jack
console.log('----------');
let s2 = new Student()
s2.getName()  // bruce
console.log(s2.like.book);  // Java

所谓的原型链继承,在我的理解就是子类通过原型链来继承到父类的属性,在上面的代码中我们也可以看到在子类实例s没有继承到父类的属性时,子类去访问父类才有的属性值为:undefined

在将父类实例对象挂载到子类Student构造函数的原型上面后,那么此时子类的原型上面就有了父类的全部属性。因为Person的实例对象被显示的挂载到了Student的原型上面。所以子类在继承之后,所有实例化的子类都拥有了跟父类一样的属性,于是这样子类就通过原型链实现了继承。

优点: 子类可以继承到父类的方法。

缺点:

  • 会在子类实例上共享父类所有的引用类型数据,也就是子类的原型被父类属性覆盖。
  • 子类实例不能给父类构造函数传参。
function Person(t) {
    this.property = t
}

function Student(t) {
    this.typeproperty = t
}
Student.prototype = new Person()
let s=new Student('a')
console.log(s.property)  // undefined
console.log(s.typeproperty)  // a

这样子类实例传的参数就不能给到父类构造函数里面了。

2. 构造函数继承

function Person(name,age) {
    this.name = name
    this.age = age
    this.like = {
        sport: 'running',
        book: 'JavaScript'
    }
    this.getName = function () {
        console.log(this.name);
    }
}
Person.prototype.sayHello = function () {
    console.log('你好');
}

function Student(name, age,) {
    Person.call(this, name, age)  // 借用构造函数
}

let s1 = new Student('bruce',18)
s1.getName()   // bruce
s1.name='jack'
s1.getName()  // jack
s1.like.sport='swimming'
console.log(s1.like.sport);  // swimming
console.log('------');
let s2=new Student('lucy',19)
s2.getName()  // lucy
console.log(s2.like.sport);  // running 

构造函数继承就是使用强制改变 this 指向,借用一个构造函数继承。相较于原型链继承,构造函数继承不会共享父类上的引用类型数据,不会互相影响。

缺点:1.不能访问到父类原型属性上面的方法和参数,即 Person.prototype 上的都不能访问到。

Person.prototype.sayHello = function () {  // 在 Person 的原型上面加一个方法
    console.log('你好');
}

let s1 = new Student('bruce',18)
s1.sayHello()  // s1.sayHello is not a function

3. 组合继承(伪经典继承)

相较于原型链继承,构造函数继承好像已经非常完美了,但是它还是存在着一个最大的缺点就是:不能继承到父类的原型上面的属性。但是原型链继承就好像没有这个问题,如果将这两个继承方式组合起来搭配使用是不是就能解决掉所有问题,于是这就有了组合继承。

function Person(name, age) {
    this.name = name
    this.age = age
    this.like = {
        sport: 'running',
        book: 'JavaScript'
    }
    this.getName = function () {
        console.log(this.name);
    }
}
Person.prototype.sayHello = function () {
    console.log('你好');
}

function Student(name, age) {
    Person.call(this, name, age)  // 构造函数继承
}
Student.prototype = new Person()  // 原型链继承

let s1 = new Student('bruce', 18)
s1.sayHello() // 你好
console.log(s1);

上面先在子类内使用构造函数继承将父类的显示属性挂载到子类上面,此时仍然存在着构造函数继承中子类不能访问到父类原型上面属性的缺点。

所以下面配合原型链继承将父类实例挂载到子类的原型上,此时子类就可以访问父类的原型上的属性,同时构造函数继承将子类的原型被父类属性覆盖的问题解决。

组合继承将前面两种继承的缺点都补全了,但是它也有缺点:

不足:重复调用两次父类函数。

4. 原型式继承

function object(o){  // 参数为父类  
    function F(){}  
    F.prototype=o  
    return new F()  
}  
  
const person={  
    sex:'man',  
    age:20  
}  
  
let realPerson=object(person)  
console.log(realPerson.sex);// man  

通过在一个构造函数的原型上挂载父类,此时再将这个挂载了父类的构造函数实例返回出来,那么这个实例的隐式原型就为这个父类,所以此时子类实例对象可以访问到父类上的属性,这就是原型式继承。这个看起来跟理解起来是不是跟原型链继承很像,也挺好理解的。

缺点: 如果父类属性有引用类型,那么这个引用类型也会被共享出来。

 function object(o){  
    function F(){}  
    F.prototype=o  
    return new F()  
}  
  
const person={  
    sex:'man',  
    age:20,  
    like:{  
        sports:'running'  
    }  
}  
  
let r1=object(person)  
console.log(r1.like);// { sports: 'running' }  
let r2=object(person)  
r2.like.sports='singing'  
console.log(r1.like); // { sports: 'singing' }  

实例化两个不同的对象,更改 r2 的sports 属性,结果 r1 的 sports 属性也被修改。

5. 寄生式继承

这种继承方式跟原型式继承有着异曲同工之妙,只不过可以增强这个父类。其存在的缺点也与原型式继承一样。

 function createPerson(original) {  
    var clone = Object.create(original)  
    clone.say = function() {     // 增强这个对象  
        console.log('hello');  
    }  
    return clone  
}  

6.寄生组合式继承

这个是一个比较完美的继承方式,使用组合继承与寄生继承。

function Person(name, age) {
    this.name = name
    this.age = age
    this.like = {
        sport: 'running',
        book: 'JavaScript'
    }
    this.getName = function () {
        console.log(this.name);
    }
}
Person.prototype.sayHello = function () {
    console.log('你好');
}

function Student(name, age) {
    Person.call(this, name, age)  // 借用构造函数
}
const Fn=function(){}
Fn.prototype=Person.prototype

Student.prototype = new Fn()



let s1 = new Student('bruce', 18)
s1.sayHello() // 你好
console.log(s1);

这种继承方式结合了组合继承,借助一个空的构造函数,将父类的原型挂载到这个空的构造函数上面,然后将其附在子类的原型上面,这样就解决了组合式继承的缺点。

7.class 类继承

Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法。

class Person{
    constructor(name){
        this.name=name
    }
}

class Student extends Person{
    constructor(name,age){
        super(name)
        this.age=age
    }
}

let s=new Student('bruce',20)
console.log(s);  //Student { name: 'bruce', age: 20 }

以上就是JavaScript实现继承的7种方式总结的详细内容,更多关于JavaScript继承的资料请关注脚本之家其它相关文章!

相关文章

  • js DOM模型操作

    js DOM模型操作

    文档对象模型DOM(Document Object Module)定义了用户操作文档对象的接口,它使得用户对HTML有了空前的访问能力,并使开发者能将HTML作为XML文档来处理。
    2009-12-12
  • 使用layui日期控件laydate对开始和结束时间进行联动控制的方法

    使用layui日期控件laydate对开始和结束时间进行联动控制的方法

    今天小编就为大家分享一篇使用layui日期控件laydate对开始和结束时间进行联动控制的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-09-09
  • 基于JavaScript实现表格滚动分页

    基于JavaScript实现表格滚动分页

    这篇文章主要为大家详细介绍了基于JavaScript实现表格滚动分页,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • JS简单测试循环运行时间的方法

    JS简单测试循环运行时间的方法

    这篇文章主要介绍了JS简单测试循环运行时间的方法,涉及针对javascript中for循环、for...in循环及foreach循环的相关使用方法及运行时间测试,需要的朋友可以参考下
    2016-09-09
  • JS前端的内存处理的方法全面详解

    JS前端的内存处理的方法全面详解

    这篇文章主要为大家介绍了JS前端的内存处理的方法全面详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • js如何操作localstorage

    js如何操作localstorage

    这篇文章主要介绍了js如何操作localstorage,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • JavaScript实现同步于本地时间的动态时间显示方法

    JavaScript实现同步于本地时间的动态时间显示方法

    这篇文章主要介绍了JavaScript实现同步于本地时间的动态时间显示方法,实例分析了javascript获取本地时间及动态显示的技巧,并对实现代码进行了较为详尽的分析说明,需要的朋友可以参考下
    2015-02-02
  • bootstrap table 多选框分页保留示例代码

    bootstrap table 多选框分页保留示例代码

    在使用bootstrap table的复选框功能的时候,由于采用服务端分页,当在第一页选择了某些数据,然后点击第二页选择一些数据,再次点回第一页,发现原先选择的数据已经清空了,原来的多选框并不支持翻页保留多选数据,怎么解决呢,下面小编给大家分享下解决思路
    2017-03-03
  • JavaScript变量声明的var、let、const详解

    JavaScript变量声明的var、let、const详解

    JavaScript中的变量是松散类型的,可以保存任何类型数据,变量只不过是一个名称,下面这篇文章主要给大家介绍了关于JavaScript变量声明的var、let、const的相关资料,需要的朋友可以参考下
    2022-07-07
  • JS如何实现页面截屏功能实例代码

    JS如何实现页面截屏功能实例代码

    这篇文章主要给大家介绍了关于JS如何实现页面截屏功能的相关资料,文中主要利用了html2canvas和canvas绘制两个方法来实现,需要的朋友可以参考下
    2021-06-06

最新评论