常年混迹于茶水间的前端攻城狮日常记录
最近在看一个系列的文章,里面特别讲到多种继承方式,之前对继承这块的总觉得不够清晰。在这里记录整理一下,方便之后复习(内容上和原文章大部分是一样的,毕竟这是篇读书笔记的读书笔记)
这一系列文章都不错,好在讲的够明白。
原文章:👉 JavaScript深入之继承的多种方式和优缺点
原型链继承
🌼 总结:Child 的原型指向 Person 的实例
function Person() {
this.name = 'Alice'
}
Person.prototype.say = function() {
console.log(`I'm ${this.name}`)
}
function Child() { }
Child.prototype = new Person()
let child = new Child()
console.log(child.name) // Alice
child.say() // I'm Alice
⚠️ 缺点
-
引用类型的属性会被所有实例共享
function Person() { this.name = 'Alice' this.firend = ['Bonnie', 'Stanfen', 'Wenry'] } Person.prototype.say = function() { console.log(`I'm ${this.name}`) } function Child() { } Child.prototype = new Person() let child = new Child() // 引用类型 child.firend.push('Bob') // 我们看看值类型 child.name = 'newName' let child2 = new Child() console.log(child2.firend) // ["Bonnie", "Stanfen", "Wenry", "Bob"] // 你看值类型就不会 console.log(child2.name) // Alice -
在创建 Child 的实例时,不能向 Person 传参
借用构造函数(经典继承)
🌼 总结:通过使用 call 或者 apply 来将父类引入子类
function Person() {
this.name = 'Alice'
this.firend = ['Bonnie', 'Stanfen', 'Wenry']
}
function Child() {
Person.call(this)
}
let child = new Child()
console.log(child.name) // Alice
console.log(child.firend) // ["Bonnie", "Stanfen", "Wenry"]
✔️ 优点
-
避免引用类型属性值被所有实例共享问题
function Person() { this.name = 'Alice' this.firend = ['Bonnie', 'Stanfen', 'Wenry'] } function Child() { Person.call(this) } let child = new Child() child.name = 'newName' child.firend.push('Bob') let child2 = new Child() // 这次引用类型也没有改变 console.log(child2.firend) // ["Bonnie", "Stanfen", "Wenry"] console.log(child2.name) // Alice -
可以在
Child中向Person传参function Person(age) { this.name = 'Alice' this.firend = ['Bonnie', 'Stanfen', 'Wenry'] this.age = age } function Child(age) { Person.call(this, age) } let child = new Child(19) console.log(child.age) // 19
⚠️ 缺点
此方法都在构造函数中定义,每次创建实例都会创建一遍方法
组合继承
🌼 总结:原型链继承 + 经典继承
function Person(name) {
this.name = name
this.firend = ['Bonnie', 'Stanfen', 'Wenry']
}
Person.prototype.say = function() {
console.log(`I'm ${this.name}`)
}
function Child(name, age) {
// 经典继承
Person.call(this, name)
this.age = age
}
// 原型链继承
Child.prototype = new Person()
// 根据原型的关系图,这个时候 Child 原型指向的构造函数变成了 Person ,这是原型链继承的副作用
// 我们需要指回来
Child.prototype.constructor = Child
let child = new Child('Alice', 19)
console.log(child.name) // Alice
console.log(child.age) // 19
child.say() // I'm Alice
✔️ 优点
解决了传参问题和引用类型被所有实例共享的问题
function Person(name) {
this.name = name
this.firend = ['Bonnie', 'Stanfen', 'Wenry']
}
Person.prototype.say = function() {
console.log(`I'm ${this.name}`)
}
function Child(name, age) {
// 经典继承
Person.call(this, name)
this.age = age
}
Child.prototype = new Person()
Child.prototype.constructor = Child
let child = new Child('Alice', 19)
child.firend.push('Bob')
let child2 = new Child('Bob', 20)
console.log(child2.firend) // ["Bonnie", "Stanfen", "Wenry"]
原型式继承
🌼 总结:将传入的对象作为创建的对象的原型。其实想想特别像在创建对象(除 null)的时候引擎会自动关联一个对象一样,只是这里我们手动给关联。这也是 Object.create() 的模拟实现。
function createObj(o) {
function F() {}
F.prototype = o
return new F()
}
let person = {
name: 'Alice',
colors: ['pink', 'red', 'blue']
}
let p1 = createObj(person)
console.log(p1.name) // Alice
console.log(p1.colors) // ["pink", "red", "blue"]
⚠️ 缺点
引用类型会被共享
function createObj(o) {
function F() {}
F.prototype = o
return new F()
}
let person = {
name: 'Alice',
colors: ['pink', 'red', 'blue']
}
let p1 = createObj(person)
p1.colors.push('green')
let p2 = createObj(person)
console.log(p2.colors) // ["pink", "red", "blue", "green"]
寄生式继承
🌼 总结:创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,然后返回对象。
function createObj(o) {
let clone = Object.create(o)
clone.say = function() {
console.log('Hi~')
}
return clone
}
let person = {
name: 'Alice',
colors: ['blue', 'red', 'pink']
}
let p1 = createObj(person)
console.log(p1.name) // Alice
p1.say() // Hi~
⚠️ 缺点
- 每次创建都会创建一遍方法,经典继承一样
- 引用类型共享
function createObj(o) {
let clone = Object.create(o)
clone.say = function() {
console.log('Hi~')
}
return clone
}
let person = {
name: 'Alice',
colors: ['blue', 'red', 'pink']
}
let p1 = createObj(person)
p1.colors.push('green')
let p2 = createObj(person)
console.log(p2.colors) // ["blue", "red", "pink", "green"]
记录完毕!