JavaScript之原型链与原型链继承
阅读原文时间:2023年07月13日阅读:1

原型链

定义:每个实例对象(object)都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(__proto__),层层向上直到一个对象的原型对象为 null

  prototype:(原型对象)函数独有的属性

  constructor:(构造函数)每个函数都有一个原型对象,该原型对象有一个 constructor 属性,指向创建对象的函数本身。

  __proto__:(原型)对象的私有属性(在 JavaScript 中一切皆对象)

原型链图解:

继承方式(es5):

方式1:基于原型链继承

弊端:当原型中存在引用类型的值时,实例能够修改这个引用类型的值(不够安全)

// 新建父类 Base
function Base() {
this.name = 'tomni'
// jobInfo为一个引用类型
this.jobInfo = {
jobName: 'job1',
price: 1000
}
}

// 在原型上添加 getName 方法
Base.prototype.getName = function() {
return this.name
}

// 创建子类
function Sub() {
this.age = 18
}
const newBase = new Base()
/**
* 原型链继承
* 1. 将 Sub 的原型对象指向 newBase
* 2. 将 Sub 原型对象的构造函数执行本身(Sub)
*/
Sub.prototype = newBase
Sub.prototype.constructor = Sub

/** *** 原型链继承的问题 *** */
const newSub = new Sub()
const newSub2 = new Sub()
// 修改实例 newSub.jobInfo 中的 jobName
newSub.jobInfo.jobName = 'job22'
// 打印 newSub2.jobInfo.jobName
// 会发现 newSub2.jobInfo 这个对象也被修改了
console.log(newSub2.jobInfo) // -> job22

方式2:更改函数 this 指向实现继承

弊端:无法继承父对象中的原型对象(prototype)中的属性和方法,每次实例化都需要执行一遍父对象,性能欠佳

// 新建父类 Base
function Base() {
this.name = 'tomni'
// jobInfo为一个引用类型
this.jobInfo = {
jobName: 'job1',
price: 1000
}
}

// 在原型上添加 getName 方法
Base.prototype.getName = function() {
return this.name
}

// 创建子类
function Sub() {
// 使用当前上下文 this 执行 Base 函数(每次对 Sub 实例化都需要执行)
Base.call(this)
this.age = 18
}

const newSub = new Sub()

// 改变this指向的方式实现继承,无法继承其原型对象(prototype)中的方法与属性
// Uncaught TypeError: newSub.getName is not a function
console.log(newSub.getName())

方式3:组合式继承

弊端:每次创建子实例,父对象原型对象中的构造函数会被执行两次

// 新建父类 Base
function Base() {
this.name = 'tomni'
// jobInfo为一个引用类型
this.jobInfo = {
jobName: 'job1',
price: 1000
}

// 会执行两次(因为创建 Sub 实例的过程中,Base.prototype.constructor 被执行了两次)  
console.log('constructor')  

}

// 在原型上添加 getName 方法
Base.prototype.getName = function() {
return this.name
}

// 创建子类
function Sub() {
// 修改 this 指向实现继承
Base.call(this)
this.age = 18
}

// 原型链继承
Sub.prototype = new Base()
Sub.prototype.constructor = Sub

const newSub = new Sub()
const newSub2 = new Sub()

// 父对象中 引用类型 被影响的问题得以解决
newSub.jobInfo.jobName = '1111'
console.log(newSub2.jobInfo.jobName)

// 无法继承 父对象原型对象中的属性和方法 问题得以解决
console.log(newSub.getName()) // -> tomni