本篇博客接着【JS系列】继承的这6种方式!(下)继续介绍你想知道的后三种继承方式,尤其是最理想的寄生组合式继承。
4. 原型式继承
4.1 介绍
原型式继承的基本思想:
- 1个基础对象
- 1个新对象,把基础对象作为原型对象
- 新对象创建实例
//基础对象
var person = {
name: "一灯",
arr: [1,2,3]
}
//Object.create()创建新对象,传入基础对象
var son1 = Object.create(person)
son1.name = "AAA"
son1.arr.push(4)
console.log(son1.name) //AAA
var son2 = Object.create(person)
son2.name = "BBB"
console.log(son2.name) //BBB
console.log(son2.arr) //1,2,3,4,引用类型问题依然存在
当然你也可以使用Object.create()的第二个参数传添加对象属性
var person = {
name: "一灯",
arr: [1,2,3]
}
var son1 = Object.create(person, {
name: {
value: "AAA"
}
})
son1.arr.push(4)
console.log(son1.name) //AAA
var son2 = Object.create(person, {
name: {
value: "BBB"
}
})
console.log(son2.name) //BBB
console.log(son2.arr) //1,2,3,4
4.2 优劣分析
-
原型式继承解决了原型链无法传参的问题,并且无需使用构造函数(避免了构造函数的问题)。因此在没必要使用构造函数时可以采用这种方法。
-
引用类型问题依旧存在
5. 寄生式继承
5.1 介绍
寄生式继承可以理解为是原型式继承的增强。在原型式继承中我们创建了一个新对象,寄生式继承便是在新对象中添加方法,以增强对象。
var person = {
name: "一灯",
arr: [1,2,3]
}
//增强对象
function increase(obj, prop) {
var object = Object.create(obj,prop)
object.getName = function() {
console.log(this.name)
}
return object
}
var son1 = increase(person, {
name: {
value: "AAA"
}
})
son1.arr.push(4)
console.log(son1.name) //AAA
son1.getName() //AAA
var son2 = increase(person, {
name: {
value: "BBB"
}
})
console.log(son2.name) //BBB
console.log(son2.arr) //1,2,3,4
son2.getName() //BBB
5.2 缺陷
寄生式继承类似于构造函数,每个实例对象都有一个副本——破坏了复用性。
6. 寄生组合式继承——大招来了
6.1 介绍
在继承的这6种方式!(上)中讲到的组合式继承比较常用的一种方式,然而这种方式还是存在一个问题——父级构造函数调用了两次(可以到上篇博文查看代码)。一次在创建子级原型对象,另一次在子级构造函数内部。
对于精益求精的码农,当然不能容忍,因此诞生了寄生组合式继承,其核心思想是:
- 组合式:子级的prototype继承父级的prototype——通过new Father()
- 寄生组合式:子级的prototype继承父级的prototype——通过赋值
function inheritPrototype(Son, Father) {
//创建一个Father.prototype的副本
var prototype = Object.create(Father.prototype)
/*下面这句代码的很多资料的描述感觉不太清晰,我的理解:
*1、Father.prototype的作用是赋值给Son.prototype
*2、如果没有下面这条语句:Son.prototype.constructor == Father构造函数
*3、因此需要更改Son.prototype.constructor,模拟new Father()的过程
*/
prototype.constructor = Son
//把Father.prototype赋值给 Son.prototype
Son.prototype = prototype
}
function Father(name) {
this.name = name
this.arr = [1,2,3]
}
Father.prototype.getName = function() {
console.log(this.name)
}
function Son(name, age) {
Father.call(this, name)
this.age = age
}
inheritPrototype(Son, Father)
Son.prototype.getAge = function() {
console.log(this.age)
}
var son1 = new Son("AAA", 23)
son1.getName() //AAA
son1.getAge() //23
son1.arr.push(4)
console.log(son1.arr) //1,2,3,4
var son2 = new Son("BBB", 24)
son2.getName() //BBB
son2.getAge() //24
console.log(son2.arr) //1,2,3
6.2 优劣分析
-
寄生组合式继承对于引用类型的继承来说是最理想的继承方式:避免了应用类型问题,并且只调用一次父级构造函数。
-
要说其缺点就是比其他方式更为复杂一些
总结
- JS共有6种方式实现继承:
- 原型链:最原始的继承方式(引用类型值相互影响、无法向父级构造函数传参)
- 借用构造函数:解决原型链的问题,但破坏了复用性
- 组合式:原型链+借用构造函数(取长避短),但调用了两次父级构造函数
- 原生式:解决原型链传参问题,并且无需使用构造函数,但也存在引用类型问题
- 寄生式:原生式的增强
- 寄生组合式:寄生式+组合式,解决了各种问题,只是代码稍微复杂
- 理解原型/原型链对理解继承帮助甚大,强烈建议先弄得JS原型——一张图彻底KO原型链(prototype,__proto__)
更多优质文章将持续更新,来关注一波……