这是我参与更文挑战的第16天,活动详情查看:更文挑战
盗用构造函数
- 在子类的构造函数中,使用
call()
来调用父类的构造函数
function SuperType(name) {
this.name = name
console.log(this)
}
SuperType.prototype.getName = function () {
return this.name
}
function SubType() {
// 继承 SuperType 并传参
SuperType.call(this, 'Nicholas')
// 实例属性
this.age = 29
}
let instance = new SubType()
console.log(instance)
复制代码
call()
方法用来改变this
的指向,这里在SubType
中执行SuperType
,将this
传递过去
- 这种方式只能继承父类的属性,不能访问原型上定义的属性方法
SuperType.prototype.getName = function () {
return this.name
}
console.log(instance.getName()) //Uncaught TypeError:
复制代码
组合继承
- 继承原型对象的属性和方法,通过
call()
盗用构造函数,继承构造函数中的属性和方法
function SuperType(name) {
this.name = name
this.colors = ['red', 'black']
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name, age) {
SuperType.call(this, name)
this.age = age
}
SubType.prototype = new SuperType()
SubType.prototype.sayAge = function () {
console.log(this.age)
}
let instance1 = new SubType('Nicholas', 29)
console.log(instance1)
instance1.colors.push('black')
console.log(instance1.colors) // "red,blue,green,black"
instance1.sayName() // "Nicholas";
instance1.sayAge() // 29
复制代码
分析一下这个对象结构:
首先需要知道,每次创建一个实例对象的时候,都会在实例中创建一个私有属性__proto__
指向构造函数的原型对象
-
SubType.prototype = new SuperType()
,这里调用SuperType
的构造函数,返回了一个实例对象,这个对象里面有两个属性{name:undefined,colors:['red',black]}
,同有一个__proto__
指向了原型对象SuperType.prototype
这个原型对象中包含一个sayName()
-
let instance1 = new SubType('Nicholas', 29)
创建实例对象,在构造函数SubType
中,使用call()
来重新指定this
,并且调用了父类SuperType
,得到了{name:'Nicholas',colors:['red',black]}
然后执行
this.age
,添加了一个age
属性。同样的他也有__proto__
指向了SubType
原型对象,这个原型对象上存在一个sayAge
方法
组合继承是使用最多的继承模式,填补了盗用构造函数继承模式不能使用父类原型上的方法的缺点
原型式继承
与前面两种模式不同的是,这种继承方式不需要创建构造函数,只需要一个简单的对象
let person = {
name: 'Nicholas',
friends: ['Shelby', 'Court', 'Van'],
}
let anotherPerson = Object.create(person)
anotherPerson.name = 'Greg'
anotherPerson.friends.push('Rob')
let yetAnotherPerson = Object.create(person)
yetAnotherPerson.name = 'Linda'
yetAnotherPerson.friends.push('Barbie')
console.log(person)
console.log(anotherPerson)
console.log(yetAnotherPerson)
复制代码
-
这里
Object.create()
返回一个新对象,这个新对象的原型对象是传入的第一个参数 -
通过控制台可以看到,
anotherPerson
是创建的一个新对象,然后里面有一个__proto__
指向它的原型对象person
原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合。但要记住,属性中包含的引用值始终会在相关对象间共享,跟使用原型模式是一样的。
寄生式继承
创建一个实现继承的函数,然后传入的对象进行一些操作,然后返回出去
function createAnother(original) {
let clone = Object.create(original) // 通过调用函数创建一个新对象
clone.sayHi = function () {
// 以某种方式增强这个对象
console.log('hi')
}
return clone // 返回这个对象
}
let person = {
name: 'Nicholas',
friends: ['Shelby', 'Court', 'Van'],
}
let anotherPerson = createAnother(person)
console.log(anotherPerson)
anotherPerson.sayHi() // "hi"
复制代码
- 与原型式继承相似,相比与他,寄生式继承知识在外面额外定义了一个函数
createAnother()
,这本身就是利用工厂函数创建对象 - 在这个工厂函数内部定义了一个
sayHi()
,Object.create()
返回一个新对象,这个新对象的原型对象是传入的第一个参数
寄生式组合继承
- 组合式继承是常用的继承方式,但是存在一个缺点,通过产看原型链可以看到,她有的属性会重复存在,在上面的例子中存在两组
name
和color
属性 - 而寄生式组合继承通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路是不通过调
用父类构造函数给子类原型赋值,而是取得父类原型的一个副本。说到底就是使用寄生式继承来继承父
类原型,然后将返回的新对象赋值给子类原型。
function inheritPrototype(subType, superType) {
let prototype = Object.create(superType.prototype) // 创建对象
prototype.constructor = subType // 增强对象
subType.prototype = prototype // 赋值对象
}
function SuperType(name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name, age) {
SuperType.call(this, name)
this.age = age
}
inheritPrototype(SubType, SuperType)
SubType.prototype.sayAge = function () {
console.log(this.age)
}
const instance = new SubType('Tom', 22)
console.log(instance)
复制代码
分析一下这个结构:
-
new SubType('Tom', 22)
调用SubType()
构造函数,遇到call()
之后改变this
指向去调用SuperType()
构造函数,创建了两个属性name
和colors
,将这个属性挂载到实例对象上,继续执行,挂载age
属性, -
inheritPrototype
创建父类原型的一个副本 ,原型对象中包含一个constrcutor
属性,这个属性指向构造函数,重新定向prototype
属性会造成constructor
属性丢失的问题,所以需要重新给新创建的对象添加一个constructor
属性