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实现继承的五种方法及原型的继承关系

继承是javascript中实现代码复用的一种方式,也能绑定对象或者函数之间的关系

为什么要继承

比如以下代码,Person、Student和Teacher构造函数,可以发现他们有一些特征

  • Person和Student都有姓名、年龄的属性和吃的方法,但Student还有学号、分数的属性和学习的方法
  • Person和Teacher都有姓名、年龄的属性和吃的方法,但Teacher还有教学的方法
function Person(name, age) {
  this.name = name
  this.age = age
  this.eating = function () {
    console.log('eating')
  }
}

function Student(name, age, sno, score) {
  this.name = name
  this.age = age
  this.sno = sno
  this.score = score
  this.eating = function () {
    console.log('eating')
  }
  this.studing = function () {
    console.log('studing')
  }
}

function Teacher(name, age) {
  this.name = name
  this.age = age
  this.eating = function () {
    console.log('eating')
  }
  this.teaching = function () {
    console.log('teaching')
  }
}

可以发现在定义函数的时候有很多的重复代码,而且Person和Student、Teacher是包含关系,继承就是通过这种包含关系为突破口来减少的重复代码

父类原型赋值给子类

这种方式是直接把子类和父类原型指向同一个对象

Student.prototype = Person.prototype

这样会将所有添加到子元素原型上的属性和方法都添加到父元素身上,再增加一个子元素,也会拥有其它子元素的所有方法,这样实现继承的方式并不推荐

原型式继承

通过函数的prototype属性就可以实现继承关系,原型式继承就是将Student.prototype赋值为Person构造函数实例对象

图解如下

在这里插入图片描述

function Person() {
  this.name = 'alice'
}
Person.prototype.eating = function () {
  console.log('eating')
}

function Student(score) {
  this.score = score
}
var person = new Person()
Student.prototype = person
var student = new Student(88)

console.log(student)
console.log(student.name)
console.log(student.__proto__)
student.eating()

以上代码的执行结果为

在这里插入图片描述

这样Student的实例对象可以访问Person的属性和方法,但是存在一些问题

  • 通过打印,无法获取Student继承的Person属性和方法
  • 不能自定义Person中属性的值

借用构造函数继承

基于以上问题,在原型式继承的基础上来借用构造函数来解决,子函数内通过call/apply来调用父函数

function Person(name) {
  this.name = name
}
Person.prototype.eating = function () {
  console.log('eating')
}

function Student(name, score) {
  Person.call(this, name)
  this.score = score
}
var person = new Person()
Student.prototype = person
var student = new Student('kiki', 88)

console.log(student)
console.log(student.name)
console.log(student.__proto__)
student.eating()

执行结果如下

在这里插入图片描述

通过这样的方式,可以解决原型式继承存在的两个问题,一是可以从Student的实例对象上找到父元素Person的属性和方法,二是可以自定义Person属性的值。

但这样一种定义方式也存在问题

  • Person构造函数至少被执行了两次,一次是创建Person的实例对象,一次是Student构造函数中通过call调用
  • 创建的person对象也保存了一份Person的数据,但这份数据是不必要的

寄生式继承

寄生式继承结合了原型式继承和工厂函数,创建一个实现继承的函数,在函数内部操作对象并返回

var user = {
  flying: function(){
    console.log('flying')
  }
}

function createObj(name, age){
  var obj = Object.create(user)
  obj.name = name
  obj.age = age
  obj.eating = function(){
    console.log('eating')
  }
  return obj
}

var kiki = createObj('kiki', 18)
console.log(kiki)
console.log(kiki.__proto__)

执行结果如下

在这里插入图片描述

通过寄生式继承,解决了借用构造函数重复调用父类函数和保存不必要数据的问题,但它又产生了新的问题

  • 无法知道对象的类型,比如Person或者Student
  • 每个对象都保存了一份eating的方法,其实是没有必要的

寄生组合式继承

通过一个对象来链接父子函数之间的继承关系

图示如下

在这里插入图片描述

首先定义一个原型继承函数,提供三种定义的方式,可以根据项目兼容性选择

var obj = {
  name: 'alice'
}

// 方式一:setPrototypeOf
function createObject1(o) {
  var obj = {}
  Object.setPrototypeOf(obj, o)
  return obj
}

// 方式二:构造函数
function createObject2(o) {
  function Fn() { }
  Fn.prototype = o
  var obj = new Fn()
  return obj
}

var obj1 = createObject1(obj)
var obj2 = createObject2(obj)

// 方式三:Object.create
var obj3 = Object.create(obj)

console.log(Object.getOwnPropertyDescriptors(obj1.__proto__))
console.log(Object.getOwnPropertyDescriptors(obj2.__proto__))
console.log(Object.getOwnPropertyDescriptors(obj3.__proto__))

执行结果为

在这里插入图片描述

然后再将原型式继承函数添加到构造函数的继承关系中

function createObj(o) {
  function Fn() { }
  Fn.prototype = o
  return new Fn()
}
// 封装继承的函数
function inheritPrototype(subType, superType){
  subType.prototype = createObj(superType.prototype)
  Object.defineProperty(subType.prototype, 'constructor', {
    value: subType,
    configurable: true
  })
}

function Person(name, age) {
  this.name = name
  this.age = age
  this.eating = function () {
    console.log('eating')
  }
}
function Student(name, age, score) {
  Person.call(this, name, age)
  this.score = score
}

inheritPrototype(Student, Person)
Student.prototype.running = function () {
  console.log('running')
}
var student = new Student('alice', 18, 100)
console.log(student)
student.running()

执行结果如下

在这里插入图片描述

寄生组合式继承就能比较好的解决以上继承方式存在的问题

原型的继承关系

实例对象、构造函数、原型对象之间存在继承的关系

var obj = {}
function Foo() { }
var foo = new Foo()

console.log(obj.__proto__ === Object.prototype)
console.log(foo.__proto__ === Foo.prototype)

console.log(Foo.__proto__ === Function.prototype)
console.log(Function.__proto__ === Function.prototype)
console.log(Object.__proto__ === Function.prototype)

console.log(Foo.prototype.__proto__ === Object.prototype)
console.log(Function.prototype.__proto__ === Object.prototype)
console.log(Object.prototype.__proto__)

执行结果如下

在这里插入图片描述

继承关系如下

  1. 实例对象

    • 定义obj字面量相当于创建Function Object的实例,所以obj的隐式原型等于Object的显式原型
    • foo对象是构造函数Foo的实例,所以foo的隐式等于Foo的显式原型
  2. 函数对象

    • 函数Foo也是对象,是构造函数Function的实例,所以函数Foo的隐式原型等于Function的显式原型
    • 函数Function也是对象,是构造函数Function的实例,所以函数Function的隐式原型等于Function的显式原型,即Function的隐式原型与显式原型相等
    • 函数Object也是对象,是构造函数Function的实例,所以函数Object的隐式原型等于Function的显式原型
  3. 原型对象

    • Foo的显式原型也是对象,是构造函数Object的实例,所以Foo的显式原型对象的隐式原型等于Object的显式原型
    • Function的显式原型也是对象,是构造函数Object的实例,所以Function的显式原型对象的隐式原型等于Object的显式原型
    • Object的显式原型也是对象,它的隐式原型指向null

图示如下

在这里插入图片描述

上述原型关系更为详细的图解如下

在这里插入图片描述

以上就是js实现继承的五种方法及原型的继承关系,关于js高级,还有很多需要开发者掌握的地方,可以看看我写的其他博文,持续更新中~

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-15 15:45:41  更:2021-11-15 15:46:00 
 
开发: 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年5日历 -2024/5/12 15:54:09-

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