js的继承你了解多少

server/2025/3/6 18:23:56/

实现继承的方式有很多,下面我们来写常用的几种(包括但不限于原型链继承、构造函数继承、组合继承、寄生组合继承、ES6继承):

原型链继承

原型链继承通过修改子类的原型为父类的实例,从而实现子类可以访问到父类构造函数以及原型上的属性或者方法。

javascript">// 原型链继承
function Parent () {this.name = 'kobe'
}Parent.prototype.getName = function () {return this.name
}Parent.prototype.info = {age: 20,}function Child() {}Child.prototype = new Parent() 
let child = new Child()
console.log('child----', child)  // Child {}
var child2 = new Child()
console.log('child.info === child2.info', child.info === child2.info) // true
child.getName() // kobe
child2.getName() // kobe
child.info.age = 20
console.log('child1.info.age---', child.info.age) // 20
console.log('child2.info.age---', child2.info.age) // 20 我们很惊奇的发现child2的age也被改掉了

上面的例子是修改的原型对象上的引用数据类型的属性,如果改成下面这样,就不会有影响

javascript">// 原型链继承
function Parent () {this.name = 'kobe'
}Parent.prototype.getName = function () {return this.name
}Parent.prototype.info = {age: 20,
}function Child() {}Child.prototype = new Parent() 
let child = new Child()
console.log('child----', child)  // Child {}
var child2 = new Child()
console.log('child.info === child2.info', child.info === child2.info) // true
child.getName() // kobe
child2.getName() // kobe
child.info = {age: 18,num: 24
}
console.log('afterchild === child2', child.info === child2.info) // false
console.log('child1.info---', child.info) // 20 {age: 18, num: 24}
console.log('child2.info---', child2.info) // {age: 20}

因为我们刚开始child的info是引用类型,存的是相同的地址,后面直接给info了一个新对象,相当于生成了一个新对象的地址,两个info此时指向了不同的地址,互不干扰了

优点

实现逻辑简单

缺点

父类构造函数中的引用类型(比如对象/数组),会被所有子类实例共享。其中一个子类实例进行修改,会导致所有其他子类实例的这个属性值都会改变

构造函数继承

构造函数继承其实就是通过修改父类构造函数this实现的继承。我们在子类构造函数中执行父类构造函数,同时修改父类构造函数的this为子类的this。

我们直接看如何实现:

javascript">function Parent(name) {this.name = [name]
}function Child(name) {Parent.call(this, name)
}let child = new Child('kobe')
child.name.push('jordan')var child2 = new Child('james')
console.log('child---', child.name) // ['kobe', 'jordan']
console.log('child2---', child2.name) // ['james'] 属性互不影响,但是方法是能各写各的,方法不通用
优点

解决了原型链继承中构造函数引用类型共享的问题,同时可以向构造函数传参(通过call传参)

缺点

所有方法都定义在构造函数中,每次都需要重新创建,方法无法复用(对比原型链继承的方式,方法直接写在原型上,子类创建时不需要重新创建方法)

所以为了解决原型链继承和构造函数继承的问题,我们决定把二者优点合一

组合继承

同时结合原型链继承、构造函数继承就是组合继承了。

javascript">function Parent () {this.name='kobe'
}Parent.prototype.getName = function () {return this.name    
}function Child () {Parent.call(this)this.num = 24
}Child.prototype = new Parent() 
Child.prototype.constructor = Child
let child = new Child()
console.log(child) // {name: 'kobe', num: 24}
console.log('Child.prototype.__proto__ = Parent.prototype', Child.prototype.__proto__ === Parent.prototype) // true

此时child为:

在这里插入图片描述

可以看到它的对象上有name属性,原型对象上也有name属性

优点

同时解决了构造函数引用类型的问题,同时解决了方法无法共享的问题

缺点

父类构造函数被调用了两次(第一次是new Parent(), 第二次是Parent.call(this))。同时子类实例以及子类原型对象上都会存在name属性。虽然根据原型链机制,并不会访问到原型对象上的同名属性,但总归是不美。

寄生组合继承

寄生组合继承其实就是在组合继承的基础上,解决了父类构造函数调用两次的问题。我们来看下如何解决的:
第一种写法:

javascript">function Parent () {this.name = 'kobe'
}Parent.prototype.getName = function () {return this.name
}
function Child () {Parent.call(this)this.num = 24
}clone(Child, Parent)
function clone (Child, Parent) {Child.prototype = Object.create(Parent.prototype)Child.prototype.constructor = Child
}let child  = new Child()
console.log('child-----', child)

第二种写法:

javascript">function Parent () {this.name = 'kobe'
}Parent.prototype.getName = function () {return this.name
}
function Child () {Parent.call(this)this.num = 24
}clone(Child, Parent)// 这个函数的作用可以理解为复制了一份父类的原型对象
// 如果直接将子类的原型对象赋值为父类原型对象
// 那么修改子类原型对象其实就相当于修改了父类的原型对象
function clone2 (o) {function F() {}F.prototype = oreturn new F()   
}
function clone (Child, Parent) {Child.prototype = clone2(Parent.prototype)Child.prototype.constructor = Child
}let child  = new Child()
console.log('child-----', child)

其实这两种的本质是一样的,都是把Parent.prototype给Child.prototype,而不是把Parent给Child.prototype
此时child为:
在这里插入图片描述
此时原型对象上已经没有同名的name属性了

优点

这种方式就解决了组合继承中的构造函数调用两次,构造函数引用类型共享,以及原型对象上存在多余属性的问题。是推荐的最合理实现方式(排除ES6的class extends继承)

缺点

没有啥特别的缺点

ES6继承

ES6提供了class语法糖,同时提供了extends用于实现类的继承,这是项目中最常见的继承方式。

使用class继承很简单,也很直观:

javascript">class Parent {constructor (name) {this.name = name}getName () {return this.name}
}class Child extends Parent {constructor (name) {super(name)this.num = 24}
}const child1 = new Child('kobe')
const child3 = new Child('jordan')
console.log('child1', child1) // {name: 'kobe', num: 24}
console.log('child3', child3) // {name: 'jordan', num: 24}
console.log('child1.getName()', child1.getName()) // kobe
console.log('child3.getName()', child3.getName()) // jordan

补充:

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

javascript">const person = {name :'kobe'}
const player = Object.create(person); // player.__proto__ === person

proto必填参数,是新对象的原型对象,如上面代码里新对象player__proto__指向person。注意,如果这个参数是null,那新对象就彻彻底底是个空对象,没有继承Object.prototype上的任何属性和方法,如hasOwnProperty()、toString()等。


http://www.ppmy.cn/server/172940.html

相关文章

Flink事件时间和处理时间咋区分

Flink事件时间和处理时间咋区分?小白也能懂😉 嘿,各位小伙伴!今天咱们来聊聊Flink里事件时间和处理时间这俩让人有点迷糊的概念🧐 别担心,我会尽量用通俗易懂的方式给大家讲清楚,就像咱们平时聊…

RabbitMQ的四种交换机

RabbitMQ交换机 什么是RabbitMQ RabbitMQ 是一个开源的消息代理和队列服务器,用于在分布式系统中存储和转发消息。它基于 AMQP(高级消息队列协议)实现,支持多种消息传递模式,广泛应用于异步通信、应用解耦、负载均衡…

API返回的数据格式是怎样的?

根据搜索结果,以下是1688按图搜索商品(拍立淘)API返回的数据格式的详细说明: API返回的数据格式 1688按图搜索商品(拍立淘)API返回的数据通常是一个JSON格式的响应,其结构如下: {&…

从0搭建Tomcat第二天:深入理解Servlet容器与反射机制

在上一篇博客中,我们从0开始搭建了一个简易的Tomcat服务器,并实现了基本的HTTP请求处理。今天,我们将继续深入探讨Tomcat的核心组件之一——Servlet容器,并介绍如何使用反射机制动态加载和管理Servlet。 1. Servlet容器的作用 S…

第六节:基于Winform框架的串口助手小项目---收发数据《C#编程》

1.目标:实现数据的收发 2.代码演示 根据控件编程 (1)接收配置 自动清空 private void autoclear_chb_CheckedChanged(object sender, EventArgs e) {if (autoclear_chb.Checked){timer1.Start();}else{timer1.Stop();} } 手动清空 privat…

vscode 配置debug的环境

vscode配置debug的环境 配置好python解释器, ctrl shift P 就可以指定python了。 当前环境下建立 .vscode 文件夹新建 .vscode/launch.json 文件文件的配置如下 {"version": "0.2.0","configurations": [{"name": &qu…

Git安装部署

1、下载Git安装包 官网地址:https://git-scm.com/ ,选择你所需要的git安装包 2、安装GIT 除了安装路径外,保持默认,一路next。 3、检查是否安装成功 WinR cmd,打开命令提示符,输入git --version&#xf…

每日学习Java之一万个为什么?[MySQL面试篇]

分析SQL语句执行流程中遇到的问题 前言1 MySQL是怎么在一台服务器上启动的2 MySQL主库和从库是同时启动保持Alive的吗?3 如果不是主从怎么在启动的时候保证数据一致性4 ACID原则在MySQL上的体现5 数据在MySQL是通过什么DTO实现的6 客户端怎么与MySQL Server建立连接…