JavaScript之原型链与原型链继承
原型链
定义:每个实例对象(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