曾经在学习js高级程序设计中,关于js如何实现继承总结过多种实现继承的方式:像原型链、构造函数、组合式、寄生式等等。当年自认为学懂了现在想起却是脑里一片空白。我不禁反问自己,你真的懂吗?实现的每一行代码都能找到依据吗? 至此,我再次梳理了关于继承的内容从最底层的角度总结了两种方法。
🌰:d1 是 Dog 的实例,d1 拥有 Dog 的所有属性与方法。d1 也想拥有 Animal 的所有属性与方法该怎么办?让 Dog 继承了 Animal。
请先忘记你所知道的所有实现继承的方式来观看最佳哦
使用原型链 (ES5)
构造函数的结构就是把对象本身的属性写在构造函数中,把共有属性写在原型上
function Animal(legsNumber){
this.legsNumber = legsNumber
}
Animal.prototype.run = function(){
console.log('hi,我能跑~')
}
function Dog(name){
this.name = name
Animal.call(this, 4) // 关键代码1
}
Dog.prototype.__proto__ = Animal.prototype // 关键代码2
Dog.prototype.say = function(){
console.log(`汪汪汪~ 我是${this.name},我有${this.legsNumber}条腿。`)
}
const d1 = new Dog('西兰花') // Dog 函数就是一个类
console.dir(d1)
继承分两部分:
- 继承本身的属性
Animal.call(this, 4) // 调用构造函数
- 继承共有属性(原型)即扩展 Dog 的原型
我们可以通过改写 Dog.prototype 的隐藏属性 __proto__ 就可以改变 Dog 的原型(链)
Dog.prototype.__proto__ = Animal.prototype
!!!由于有些浏览器不是使用__proto__而是其他名称,被禁用怎么办?
也就是换一种方式实现原型链,我想到了 new
Dog.prototype = new Animal()
new 到底干了什么 它做了以下5件事情:
- 创建临时对象
- 指定 this = 临时对象
- 绑定原型(共有属性) 临时对象.
__proto__= 构造函数.原型 - 执行构造函数(绑定自身属性)
- 返回this 即临时对象
很明显 new 完全可以满足需求做到 绑定原型(共有属性),但是绑定自身属性是多余且错误的操作,因为Animal.call(this, 4)已经实现了:)。
是否可以借助一个空函数,把 Animal.prototype 给到空函数.prototype,那么执行这个空函数就是绑定了空的自身属性?
var f = function(){ }
f.prototype = Animal.prototype
Dog.prototype = new f()
最终使用的实现代码:
function Animal(legsNumber){
this.legsNumber = legsNumber
}
Animal.prototype.run = function(){
console.log('hi,我能跑~')
}
function Dog(name){
this.name = name
Animal.call(this, 4)
}
var f = function(){ }
f.prototype = Animal.prototype
Dog.prototype = new f()
Dog.prototype.say = function(){
console.log(`汪汪汪~ 我是${this.name},我有${this.legsNumber}条腿。`)
}
const d1 = new Dog('西兰花') // Dog 函数就是一个类
console.dir(d1) // 打印结构
有同学可能会疑惑既然 new都把事情做完了,为什么还要使用Animal.call(this, 4)绑定到 Dog 的自身属性上,然后又要创建个空数组单独处理绑定自身属性这个问题
还是看这段代码
Dog.prototype = new Animal()
执行构造函数 Animal 会将它的自身属性 legsNumber 绑定到 Dog.prototype 上面!而不是 Dog 上!!!
使用 class(ES6)
class 的结构是把对象本身的属性写constructor中,把共有属性写在constructor外面
class Animal{
constructor(legsNumber){
this.legsNumber = legsNumber
}
run(){}
}
class Dog extends Animal{ // 关键代码1
constructor(name) {
super(4) // 关键代码2
this.name = name
}
say(){
console.log(`汪汪汪~ 我是${this.name},我有${this.legsNumber}条腿。`)
}
}
继承分两部分:
- 继承本身的属性 ---- super (调用父类构造函数)
- 继承共有属性(原型)---- extends