Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Javascript 的继承总结

Javascript 的继承总结

作者头像
Meteors
发布于 2021-12-08 12:23:09
发布于 2021-12-08 12:23:09
27400
代码可运行
举报
文章被收录于专栏:星空畅想星空畅想
运行总次数:0
代码可运行

写惯了 TypeScript 的人很容易了解继承(extends),比如类的继承和接口的继承等,传送门:www.tslang.cn/docs/handbo… ,但是对于ES2015出现之前,JavaScript如何实现继承的呢?毫无疑问,只能通过原型链的方式实现继承,本篇主要是在读书时遇到了原型继承的问题,回顾以下原型继承的集中方式并整理成笔记方便日后查阅。

类式继承(原型链继承)

类式继承非常简单,总结来说就是:子类的原型的 prototype 被赋予父类的实例,从而继承父类的属性和方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义 Person 类
function Person() {
    this.name = 'zhang san'
}
// 添加成员函数
Person.prototype.getName = function() {
    console.log(this.name)
}
// 定义 Employee 类
function Employee() {
    
}
// 类式继承
Employee.prototype = new Person()
let employee1 = new Employee()
console.log(employee1.getName()) // zhang san
复制代码

并且,通过 instanceof 关键字可以判断该子类实例为父类原型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let employee1 = new Employee()
console.log(employee1 instanceof Employee) // true
console.log(employee1 instanceof Person) // true
console.log(employee1 instanceof Object) // true, 顶层原型为Object
复制代码

但是,类式继承往往会出现许多问题:

  1. 引用类型的属性被所有实例共享
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义 Person 类
function Person() {
    this.name = 'zhang san'
    this.skill = ['吃饭''睡觉']
}
// 添加成员函数
Person.prototype.getSkill = function() {
    console.log(this.skill)
}
// 定义 Employee 类
function Employee() {
    
}
// 类式继承
Employee.prototype = new Person()

let employee1 = new Employee()
employee1.skill.push('写代码')

let employee2 = new Employee()
employee2.skill.push('做决策')

console.log(employee1.getSkill()) // ['吃饭', '睡觉', '写代码', '做决策']
console.log(employee2.getSkill()) // ['吃饭', '睡觉', '写代码', '做决策']
复制代码
  1. 在创建 Employee 实例时候,无法向 Person 传递参数

构造函数继承

类式继承虽然简单,但是缺点非常明显,我们是否可以通过别的继承方式避免这些缺点呢?实际上,构造函数继承就可以解决这一点,总结来说构造函数继承就是:子类构造函数作用环境执行一次父类构造函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义 Person 类
function Person() {
    this.name = 'zhang san'
    this.skill = ['吃饭''睡觉']
}
// 添加成员函数
Person.prototype.getSkill = function() {
    console.log(this.skill)
}
// 定义 Employee 类,并继承 Person 类
function Employee() {
    Person.call(this)
}

let employee1 = new Employee()
employee1.skill.push('写代码')

let employee2 = new Employee()
employee2.skill.push('做决策')

console.log(employee1.skill) // ['吃饭', '睡觉', '写代码']
console.log(employee2.skill) // ['吃饭', '睡觉', '做决策']

// 定义 Manager 类,并继承 Person 类
function Manager(skills) {
    Person.call(this, skills)
}

let manager1 = new Manager(['吃饭', '睡觉', '做决策'])
console.log(manager1.skill) // ['吃饭', '睡觉', '做决策']

console.log(manager1.getSkill()) // 报错,没有该方法
复制代码

可以看出,构造函数继承已经完美解决了类式继承的两个问题,但是出现了一个新的问题,即:构造函数继承只继承父类属性,父类中方法不会被子类继承

组合继承

一看上面两个方式都或多或少有些问题,这就需要用到组合继承,也是JavaScript中最常见的继承方式。总结来说就是一句话,类式继承 + 构造函数继承

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义 Person 类
function Person() {
    this.name = 'zhang san'
    this.skill = ['吃饭''睡觉']
}
// 添加成员函数
Person.prototype.getSkill = function() {
    console.log(this.skill)
}
Person.prototype.getName = function() {
    console.log(this.name)
}
// 定义 Employee 类,并继承 Person 类
function Employee(name, skill) {
    Person.call(this, name, skill)
}
Employee.prototype = new Person()

let employee1 = new Employee('zhang san', ['吃饭', '睡觉', '写代码'])
console.log(employee1.getName()) // zhang san
console.log(employee1.getSkill()) // ['吃饭', '睡觉', '写代码']

let employee2 = new Employee('li si', ['吃饭', '睡觉', '做决策'])
console.log(employee2.getName()) // li si
console.log(employee2.getSkill()) // ['吃饭', '睡觉', '做决策']
复制代码

组合继承综合两种继承方式,解决了之前的两种方式产生的问题。但是实际上,在一次继承中,父类构造函数被调用了两次,这看起来令人十分费解:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 设置子类型实例的原型,调用父类构造函数
Employee.prototype = new Person()

// 创建子类实例,调用父类构造函数
Person.call(this, name, skill)
复制代码

原型式继承

有人提出了一种新的继承方式:原型式继承,总结来说就是:根据已有的对象创建一个新的对象,同时不必创建新的自定义对象类型。简单来说原型继承就是封装了下面这样一个 create 函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function create(o) {
    function F(){}
    f.prototype = o
    return new F()
}
复制代码

熟悉ES规范的朋友可以看出,这就是 Object.create()的模拟实现。缺点还是很明显,值类型属性被复制,引用类型属性被共用,跟原型链继承的问题差不多:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义 Person 类
function Person() {
    this.name = 'zhang san'
    this.skill = ['吃饭', '睡觉']
}

let person1 = create(Person)
let person2 = create(Person)

person1.name = 'li si'
console.log(person1.name) // li si

person1.skill = ['吃饭', '睡觉', '写代码']
console.log(person2.skill) // ['吃饭', '睡觉', '写代码']
复制代码

这是不是说明原型式赋值并没有什么用?可以往下继续看。

寄生式继承

寄生式继承方式就跟名字一样,总结来说就是:创建一个仅用于封嘴昂继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function create(o) {
    let clone = Object.create(o)
    clone.getName = function() {
        console.log('get name')
    }
    return clone
}
复制代码

当然,跟构造函数继承一样,不会继承父类方法,每次创建对象时都需要创建一次方法。

寄生组合式继承

寄生组合式继承,实际上就是寄生继承 + 类式继承,目的就是为了解决组合继承创建对象时调用两次父类构造方法的问题,如果不使用 Employee.prototype = new Person(),而是间接让 Employee.prototype 访问到 Person.prototype 呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function create(o) {
    function F() {}
    F。prototype = o
    return new F ()
}
function prototypr(child, parent) {
    let prototype = object(parent.prototype)
    prototype.constructor = child
    child.prototype = prototype
}

prototypr(Employee, Person)
复制代码

寄生组合方式解决了调用两次父类构造函数的问题,可以算是JavaScript终极解决方式了。

总结

本文总结了类式继承、构造函数继承、组合式继承、原型式继承、寄生式继承、寄生组合式继承,建议对原型链和继承不是了解很多的同学仔细看看,ES6 class 继承请移步《ES6入门教程》es6.ruanyifeng.com/#docs/class…

参考

  1. 《JavaScript设计模式》
  2. 《JavaScript高级程序设计(第二版)》
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021年04月11日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JavaScript中6种继承方式总结
我们在代码里的操作本意是只改变goku1的属性,但是为什么goku2的属性也被改变了呢?因为skill是一个数组,为引用类型,goku1与goku2的skill实际是指向同一块内存空间,因此修改会变得混乱。我们将在接下来的继承方式中解决这个问题。
henu_Newxc03
2022/05/05
3770
JavaScript中6种继承方式总结
javascript面向对象之继承(上)
我们之前介绍了javascript面向对象的封装的相关内容,还介绍了js的call方法,今天开始讨论js的继承 这篇文章参考了《javascript高级程序设计》(第三版),但内容不局限于,网上很多关于js继承的相关内容都是来自于这本书,有兴趣的同学可以翻阅查看
陌上寒
2019/04/02
4300
javascript面向对象之继承(上)
javascript 面向对象(实现继承的几种方式)
 1、原型链继承 核心: 将父类的实例作为子类的原型 缺点: 父类新增原型方法/原型属性,子类都能访问到,父类一变其它的都变了 function Person (name) { this.name = name; }; Person.prototype.getName = function () { //对原型进行扩展 return this.name; }; funct
柴小智
2018/04/10
7020
JavaScript 中的六种继承方式
关键点:子类原型等于父类的实例Child.prototype = new Person()
Jimmy_is_jimmy
2019/07/31
5050
ES5中的继承
面向对象的三大特性是:封装、继承、多态。其中继承是最难理解的,也是最重要的部分。 JS中本身没有专门继承的语法,它是使用各种代码的模拟来实现的。即使ES6有了正真的继承语法,其本质也是ES5中继承的语法糖。目前ES5继承最被人津津乐道的就是尼古拉斯的著名书籍《JavaScript高级程序设计》中记录的6中方法。本文也是摘自这本本书的这部分的核心内容,并整理给大家呈现出来。
kai666666
2020/10/19
3760
JavaScript 进阶教程(3)---让你彻底搞懂原型链和继承
关于原型在JavaScript 进阶教程(1)--面向对象编程这篇文章已经讲过了,今天简单来复习一下。
AlbertYang
2020/09/08
5240
JavaScript 进阶教程(3)---让你彻底搞懂原型链和继承
JavaScript实现继承
使用class继承非常简单。子类使用extends关键字表明继承于哪个类,并在子类中调用super(),这相当于使用call()改变this的指向。
不作声
2020/10/30
4130
ECMAScript中类与继承详解(Java对比学习)
如果声明一个一个类的时候没有声明构造函数,那么会默认添加一个空的构造函数,构造函数在new实例化一个对象的时候会被调用
coder_koala
2019/07/30
4420
修炼内功之JavaScript设计模式(一)
有一个简单的大局观,造完了火箭,再回归正文,我们的日常生活和工作中的大部分还是需要脚踏实地搬砖的,为了应对不断变换的需求,为了不加班,掌握设计模式的思想可以大大提高我们的搬砖效率。
童欧巴
2020/03/30
6050
修炼内功之JavaScript设计模式(一)
JavaScript实现继承的六种方式
prototype里有个属性constructor指向构造函数本身,但是, Student的原型已经被父类的实例取代了,所以指向也不正确,所以需要修复构造函数指向(这里网上的教程只是对组合继承、寄生组合式继承进行了修复,不知道是不是因为这个不常用的关系)
赤蓝紫
2023/03/11
5540
JavaScript实现继承的六种方式
【前端词典】继承
继承于我们前端来说绝对是非常熟悉也必须熟悉的一个高频必懂知识点。熟悉到只要是面试一定会有关于继承的问题;而且源码中继承的使用也随处可见。
小生方勤
2019/06/01
6330
JavaScript实现继承的6种方式
创建 Father 的实例,并赋值给 Son 的原型 Son.prototype。实现了 Son 继承 Father,
andyhu
2023/06/18
4090
JavaScript的几种继承方式
这篇文章称为笔记更为合适一些,内容来源于 《JavaScript高级程序设计 (第三版)》第六章 6.3 继承。
木子星兮
2020/07/16
5330
JavaScript实现继承
本文不准备深入细节,主要是对《JavaScript高级程序设计中》介绍的JS如何实现继承做一个总结,毕竟好记性不如烂笔头。文末会附带一张神图,搞清楚这张图,原型链也就没有什么问题了。
leocoder
2018/10/31
6750
JavaScript 原型
在 JavaScript 中,函数也是一种对象。每个函数都有一个特殊的属性,叫做 prototype。这个属性实际上就是一个普通的对象,它拥有一些方法和属性,那么这些方法和属性是如何被其他对象共享的呢?这就是 JavaScript 的原型机制做的事情。
用户7614656
2023/10/30
3021
JavaScript 面试要点: 继承
ECMA-262 把原型链定义为 ECMAScript 的主要继承方式。其基本思想就是通过原型继承多个引用类型的属性和方法。对属性和方法的搜索会一直持续到原型链的末端。
Cellinlab
2023/05/17
1910
JavaScript 面试要点: 继承
JavaScript常见的六种继承方式
面向对象编程很重要的一个方面,就是对象的继承。A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的。
Javanx
2019/09/05
7790
对Javascript 类、原型链、继承的理解
  和其他面向对象的语言(如Java)不同,Javascript语言对类的实现和继承的实现没有标准的定义,而是将这些交给了程序员,让程序员更加灵活地(当然刚开始也更加头疼)去定义类,实现继承。(以下不讨论ES6中利用class、extends关键字来实现类和继承;实质上,ES6中的class、extends关键字是利用语法糖实现的)
smy
2019/02/13
6910
对Javascript 类、原型链、继承的理解
深入浅出js实现继承的7种方式
  有些人认为JavaScript并不是真正的面向对象语言,在经典的面向对象语言中,您可能倾向于定义类对象,然后您可以简单地定义哪些类继承哪些类(参考C++ inheritance里的一些简单的例子),JavaScript使用了另一套实现方式,继承的对象函数并不是通过复制而来,而是通过原型链继承
TimothyJia
2019/11/12
2.1K0
一篇文章告诉你JavaScript 如何实现继承
JavaScript 在编程语言界是个特殊种类,它和其他编程语言很不一样,JavaScript 可以在运行的时候动态地改变某个变量的类型。
前端皮皮
2021/11/12
2140
一篇文章告诉你JavaScript 如何实现继承
相关推荐
JavaScript中6种继承方式总结
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验