IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> JS中的六种继承:原型链、盗用构造函数、组合继承、原型式继承、寄生继承、组合寄生继承 -> 正文阅读

[JavaScript知识库]JS中的六种继承:原型链、盗用构造函数、组合继承、原型式继承、寄生继承、组合寄生继承

在这里插入图片描述

图解JS中的六种继承:原型链、盗用构造函数、组合继承、原型式继承、寄生继承、组合寄生继承

继承

继承有两种:接口继承(Interface Inheritance)和实现继承(Implementation Inheritance)。接口继承需要有方法签名,JS由于没有方法签名,因此接下来我们讲解的六种继承都是属于实现继承

继承本质上就是一个对象复用另一个对象的属性和方法,注意,属性和方法的复用效果是不同的,从这点出发去理解继承,也就能理解为什么会产生六种继承。

原型链 Prototype Chaning

首先说一下原型链:

function Animal(){
    this.names = ['animal', 'zoom', 'beast', 'creature']
    this.say = function(index){
        console.log(`I am ${this.names[index]}`)
    }
}

var oneAnimal = new Animal()
oneAnimal.say(1) // I am zoom

在这里插入图片描述

每一个方法都有原型,我们可以在原型链挂载属性或者方法,原型在方法和通过new方法产生的实例之间分享:

function Animal(){
}
Animal.prototype.names = ['animal', 'zoom', 'beast', 'creature']
Animal.prototype.say = function(index){
    console.log(`I am ${this.names[index]}`)
}

var oneAnimal = new Animal()
oneAnimal.say(1) 
//> I am zoom

在这里插入图片描述

原型链最大的好处是实例间可以复用:

var anotherAnimal = new Animal()
anotherAnimal.say(2)
//> I am beast

在这里插入图片描述

接着说一下原型链是怎么继承的:

function Animal(){
}
Animal.prototype.names = ['animal', 'zoom', 'beast', 'creature']
Animal.prototype.say = function(index){
    console.log(`I am ${this.names[index]}`)
}

function Cat(){
    this.whiskers = 8
}

// 从Animal中继承
Cat.prototype = new Animal()

var kitten = new Cat()
kitten.say(2) //> I am beast

在这里插入图片描述

原型链有两个大问题,一个是引用值在实例之间互用:

var kitten = new Cat()
var oldCat = new Cat()
kitten.names.push('kitten')
oldCat.say(4) //> I am kitten

第二个问题是,在创建实例的时候,不能传参给父类构造器。

盗用构造器 Constructor Stealing

为了解决引用值的问题和无法传参给父类的情况,开发者开始使用称为盗用构造器的方法,也成为对象伪装或者经典继承(masquerading or classic inheritance)。

function Animal(myName){
    this.names = ['animal', 'zoom', 'beast', 'creature']
    this.names.push(myName)
}
Animal.prototype.say = function(index){
    console.log(`I am ${this.names[index]}`)
}

function Cat(myName){
    Animal.apply(this, [myName])
}

Cat.prototype = new Animal()

var kitten = new Cat('kitten')
kitten.say(4) //> I am kitten

在这里插入图片描述

盗用构造器是一种很有趣的比喻,它是说,把父类的构造器当成自己的构造器来使用,最重要的目的是做一份属性的副本,也就是拷贝引用值,这样就不用担心实例之间互相修改引用值。同时,在引用了父类构造器的时候传参,也解决了创建实例不能传参给父类的问题。

var kitten = new Cat('kitten')
var oldCat = new Cat('oldCat')
kitten.say(4) //> I am kitten
oldCat.say(4) //> I am oldCat

组合继承 Combination Inheritance

盗用构造器中,当我们继承父类之后,如果要添加方法(or属性)或者重写方法的话,我们自然而然会想到:

function Animal(myName){
    this.names = ['animal', 'zoom', 'beast', 'creature']
    this.names.push(myName)
}
Animal.prototype.say = function(index){
    console.log(`I am ${this.names[index]}`)
}

function Cat(myName, age){
    Animal.apply(this, [myName])
    //---------------------------------------------------------------|
    this.age = age                                                 //|
    //---------------------------------------------------------------|
}

Cat.prototype = new Animal()

//--------------------------------------------------------------------|
Cat.prototype.sayAge = function(){                                  //|
    console.log(`My age = ${this.age}`)                             //|
}                                                                   //|
//--------------------------------------------------------------------|

var kitten = new Cat('kitten', 3)
kitten.sayAge()
//> My age = 3

没错,这个就是组合继承,也称为伪经典继承(pseudoclassical inheritance),基本思想是盗用构造器+添加属性+原型链附着方法

在这里插入图片描述

组合继承比盗用构造器更加合理的解决了引用值、传参、添加属性/方法的问题,但实际上,组合继承还不够完美,它还需要两次new Animal()父类对象。

原型式继承 Prototypal Inheritance

在之前的例子中,我们需要先自定义一个Animal父类,然后再定义Cat子类去继承它,原型式继承的作用是跳过第一步,使用一个现成的类直接继承。

function create(fatherInstance){
    function Son(){}
    Son.prototype = fatherInstance
    return new Son()
}

在这里插入图片描述

这个就是原型式继承,Object.create()就是使用这种方式:

在这里插入图片描述

In 2006, Douglas Crockford wrote an article, “Prototypal Inheritance in JavaScript”.

寄生继承 Parasitic Inheritance

function parasiticCreate(fatherInstance){
    var sonInstance = create(fatherInstance)
    sonInstance.sayHi = function(){
        console.log(`Hi`)
    }
    return sonInstance
}

在这里插入图片描述

寄生继承核心实现是完成继承+给实例添加方法。

寄生组合继承 Parasitic Combination Inheritance

基础寄生组合继承:

function parasiticCombination(Father, Son){
    var middle = create(Father.prototype)
    middle.contructor = Son
    Son.prototype = middle
}

寄生组合继承:盗用构造器继承属性+寄生继承来创建一个新对象(作为子类对象的新原型)

function Animal(myName){
    this.names = ['animal', 'zoom', 'beast', 'creature']
    this.names.push(myName)
}

Animal.prototype.say = function(index){
    console.log(`I am ${this.names[index]}`)
}

function Cat(myName, age){
    Animal.apply(this, [myName])
    this.age = age
}

parasiticCombination(Animal, Cat)

Cat.prototype.sayAge = function(){
    console.log(`My age is ${this.age}`)
}

var kitten = new Cat('kitten', 3)
var oldCat = new Cat('oldCat', 9)
kitten.say(4)
oldCat.say(4)

在这里插入图片描述

对比一下组合继承,寄生组合继承中不再去new一个Animal实例作为Cat的原型,而是通过寄生继承产生一个新对象替代,因此少了一次调用父类构造器,减少了不必要的属性。

寄生组合继承是目前ES6类的实现方法,它被认为是性能最好的继承方案。

实际实现:

function Animal(myName){
    this.names = ['animal', 'zoom', 'beast', 'creature']
    this.names.push(myName)
}

Animal.prototype.say = function(index){
    console.log(`I am ${this.names[index]}`)
}

function Cat(myName, age){
    Animal.apply(this, [myName])
    this.age = age
}

var middle = Object.create(Animal.prototype)
Cat.prototype = middle

Cat.prototype.sayAge = function(){
    console.log(`My age is ${this.age}`)
}

var kitten = new Cat('kitten', 3)
var oldCat = new Cat('oldCat', 9)
kitten.say(4)
oldCat.say(4)

转换后:

class Animal{
    constructor(myName){
        this.names = ['animal', 'zoom', 'beast', 'creature']
        this.names.push(myName)
    }
    say(index){
        console.log(`I am ${this.names[index]}`)
    }
}

class Cat extends Animal{
    constructor(myName, age){
        super(myName) // 在派生类中,必须调用super(),否则会报错
        this.age = age
    }
    sayAge(){
        console.log(`My age is ${this.age}`)
    }
}

var kitten = new Cat('kitten', 3)
var oldCat = new Cat('oldCat', 9)
kitten.say(4)
kitten.sayAge()
oldCat.say(4)
oldCat.sayAge()
//> I am kitten
//> My age is 3
//> I am oldCat
//> My age is 9

所以为什么说ES6继承是原型链的语法糖,因为背后封装了好几道复杂的工艺。

总结

在这里插入图片描述

最后

最后,期待大家能够关注个人公粽号:YopenTech
主要引进国外前沿技术文章、博客,学习国外先进技术,以此师夷长技以制夷,争做一个高质量技术公众号,而不是广告混杂、哗众取宠、猎奇标题、质量低下。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-11 12:37:51  更:2021-11-11 12:38:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 10:51:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码