1. 类式继承
- 很简单的很纯粹的一种继承方式,操作也很简单,将子类的原型指向父类的实例对象即可
function Father () {
this.value = true
}
Father.prototype.getFatherValue = function() {
return this.value
}
function Son () {
this.sonValue = false
}
Son.prototype = new Father()
Son.prototype.getSonValue = function() {
return this.sonValue
}
var ex1 = new Son()
console.log(ex1.getFatherValue()) // true
console.log(ex1.getSonValue()) // true
- 继承的核心就是Son.prototype = new Father()
- 但是这种做法有个致命的缺点就是如果父类中的共有属性是一个引用类型的值时,会在所有的子类实例共用,也就是说一个子类实例修改从父类继承来的公有属性就会直接影响其他的子类。
function Father () {
this.type = ['html', 'css', 'js']
}
function Son () {}
Son.prototype = new Father()
var ex1 = new Son()
var ex2 = new Son()
console.log(ex2.type) // ['html', 'css', 'js']
ex1.type.push('ts')
console.log(ex2.type) // ['html', 'css', 'js', 'ts']
- 该方法继承还有一个缺点:由于子类实现的继承是靠其原型对父类的实例化完成的,所以在创建父类时,无法向父类传递参数,因此在实例化父类的时候,无法对父类构造函数内的属性进行初始化
2. 构造继承
- 实现的方法也很简单,只是在子类的构造函数中使用call或者apply更改父类上下文中的this
function Father (id) {
this.type = ['html', 'css', 'js']
this.id = id
}
Father.prototype.showType = function() {
return this.type
}
function Son (id) {
Father.call(this, id)
}
var ex1 = new Son(1)
var ex2 = new Son(2)
ex1.type.push('ts')
console.log(ex1.type) // ['html', 'css', 'js', 'ts']
console.log(ex1.id) // 1
console.log(ex2.type) // ['html', 'css', 'js' ]
console.log(ex2.id) // 2
console.log(ex1.showType) // undefined
- 继承的核心就是Father.call(this, id)
- 从代码中的测试中可以知道,构造继承解决了类式继承中父类共有属性是引用值的问题,但是却出现了另一个致命的缺点:父类的共有属性和方法必须写在构造函数内,父类中的原型上的方法和属性子类是无法获取的。也许,你已经发现类式继承和构造继承的可以相互解决对方的缺点,那么将两者结合不就可以了?因此出现了第三种继承方式组合继承
3. 组合继承
- 组合继承就是类式继承和构造继承组合而成的一种继承方式
function Father (name) {
this.type = ['html', 'css', 'js']
this.name = name
}
Father.prototype.getName = function() {
console.log(this.name)
}
function Son (name, time) {
Father.call(this, name)
this.time = time
}
Son.prototype = new Father()
Son.prototype.getTime = function() {
console.log(this.time)
}
var ex1 = new Son('father', 2020)
ex1.type.push('ts')
console.log(ex1.type) // ['html', 'css', 'js', 'ts']
ex1.getName() // father
ex1.getTime() // 2020
var ex2 = new Son('son', 2021)
console.log(ex2.type) // ['html', 'css', 'js']
ex2.getName() // son
ex2.getTime() // 2021
- 果然将两者结合到一起,就能够解决类式继承和构造继承产生的两个问题,但是在使用时却发现,继承时调用了一次父类构造函数,在实现子类实例化时,又一次调用了父类构造函数,突然觉得,这并不是最美完美的方法,还有更完美的方法就是寄生组合式继承,不过在此之前,应该了解一下原型继承和寄生继承
4. 原型式继承
- 先看一下实现方式
function inheritObject(o) {
function F() {}
F.prototype = o
return new F()
}
var book = {
name: 'js',
other: ['html', 'css']
}
var ex1 = inheritObject(book)
ex1.other.push('ts')
ex1.name = 'js1'
console.log(ex1.name) // js1
console.log(ex1.other) // ["html", "css", "ts"]
var ex2 = inheritObject(book)
ex2.name = 'js2'
console.log(ex2.name) // js2
console.log(ex2.other) // ["html", "css", "ts"]
- 是不是觉得和类式继承很相似,实际上原型继承就是类式继承的封装,其中的过度对象就是类式继承中的子类(Son),只是在原型继承中作为一个过度对象出现,目的就是为了创建要返回的对象实例,他的缺点自然是和类式继承一样
5. 寄生式继承
- 寄生继承简单来说就是对原型继承的二次封装,在二次封装的过程中对继承的对象进行了拓展,这样不仅拥有父类的属性和方法,还拥有新添加的属性和方法
function inheritObject(o) {
function F() {}
F.prototype = o
return new F()
}
function createBook(obj) {
var o = new inheritObject(obj)
o.getName = function() {
console.log(this.name, '---')
}
return o
}
var book = {
name: 'js',
other: ['html', 'css']
}
var ex1 = createBook(book)
ex1.other.push('ts')
ex1.name = 'js1'
ex1.getName() // js1
console.log(ex1.other) // ["html", "css", "ts"]
var ex2 = createBook(book)
ex2.name = 'js2'
ex2.getName() // js2
console.log(ex2.other) // ["html", "css", "ts"]
- 缺点和类式继承相同,但相比于原型继承,可以额外的添加方法和属性
6. 寄生组合继承
- 该继承是寄生继承和构造继承组合使用,只不过寄生组合继承中,处理的不是类,而是类的原型
function inheritObject(o) {
function F() {}
F.prototype = o
return new F()
}
function inheritPrototype(subClass, superClass) {
var p = inheritObject(superClass.prototype)
p.constructor = subClass
subClass.prototype = p
}
function SuperClass(name) {
this.name = name
this.colors = ['red', 'green', 'yellow']
}
SuperClass.prototype.getName = function() {
console.log(this.name)
}
function SubClass(name, time) {
SuperClass.call(this, name)
this.time = time
}
inheritPrototype(SubClass, SuperClass)
SubClass.prototype.getTime = function() {
console.log(this.time)
}
var ex1 = new SubClass('html', 2021)
var ex2 = new SubClass('css', 2021)
ex1.colors.push('blue')
console.log(ex1.colors) // ['red', 'green', 'yellow', 'blue']
console.log(ex2.colors) // ['red', 'green', 'yellow']
ex2.getName() // css
ex2.getTime() // 2021
- 在该继承方法中,可以看到inheritPrototype就是之前提到的寄生继承,由于复制对象p的constructor指向的不是subClass,所以需要修正一下constructor的指向为subClass,然后将对象p赋值给subClass的原型,这样子类的原型就继承了父类的原型而且没有执行父类的构造函数。SuperClass以后的代码就和构造函数很类似,多了一行**inheritPrototype(SubClass, SuperClass)**调用寄生方式,解决继承父类原型问题。
- 该方法算是几乎完美的解决方法,要说不完美,可能就是太复杂了吧
ps: 此文章做个人平常记录,若是有幸帮到朋友,但求一赞