继承,在面向对象的编程中用的还是非常多的,虽然面向对象因为其复杂的构建对象的初始化,在大多数的程序中并没用用到,但是我们使用过后才发现,这使得今后项目的扩展和维护更加容易。话不多说,进入正题。
1. 传统形式(原型链继承)
//父类
Father.prototype.name = "Li"
function Father() {
this.age = 18
}
//子类
Son.prototype = new Father()
function Son() {}
var son = new Son()
这种方法确实可以继承到name属性。son.name也确实是"Li",我们输出son.age也可以得到18。
但是Son的构造函数中并没有age属性,该属性是Son.prototype中的。
缺点:并没有把父级的age属性继承到Son的构造函数中,而是放到了Son.prototype中,这并不是我们想要的
2. 借用构造函数继承
//父类
Father.prototype.name = "Li"
function Father() {
this.age = 18
}
//子类
function Son() {
Father.call(this)
}
var son = new Son()
这种方法可以实现Son构造函数中有age属性
我们从图片中可以看出,虽然Son中是有了age属性,但是Son.prototype还是自己的原型,并没有继承自父类
缺点:1.不能继承借用构造函数的原型。2.每次用Son构造对象时,都会执行两个构造函数,造成资源浪费
3. 共享原型
//父类
Father.prototype.name = "Li"
function Father() {
this.age = 18
}
//子类
function Son() {
Father.call(this)
}
Son.prototype = Father.prototype
var son = new Son()
这种写法看似没有问题,age被放到了Son构造函数中,父类原型上的name属性也继承来了
但是我们忽略了一点,不管是Son的prototype还是Father的prototype,他们都是一个对象,对象之间的赋值是地址的传递。所以当我们改变Father原型上的属性时,Son原型也会被修改。因为Son.prototype = Father.prototype这句话使他们指向了一个内存空间
缺点:原型之间改变相互影响
4. 圣杯模式
总结了上述方式缺点之后,慢慢的就演变成了现在在用的完美的继承方式,至于"圣杯模式"这个名字的由来,大家感兴趣的话还是自己查阅资料,"圣杯"简单来说就是"永恒"的意思,这里不做过多的讨论。
//父类
Father.prototype.name = "Li"
function Father() {
this.age = 18
}
//子类
function Son() {
Father.call(this)
}
//中间构造函数
function Temp() {}
Temp.prototype = Father.prototype
Son.prototype = new Temp()
var son = new Son()
我们借用一个空的构造函数与Father共享原型,然后让Son的原型指向Temp的实例就可以完美解决以上问题。
解析:Temp.prototype = Father.prototype,使Temp.prototype和Father.prototype指向同一个内存空间,并不会影响Son.prototype。此时Son.prototype = new Temp()相当于Son.prototype__proto__ = Temp.prototype,也就是Son.prototype__proto__ = Father.prototype。从而实现了继承。
优化:
- 我们可以将它封装为一个方法,因为每次继承都会创建一个新的Temp中间函数,造成了资源浪费。优化代码如下
var inherit = (function () {
var Temp = function() {}
return function (Target, Origin) { //target继承自origin
Temp.prototype = Origin.prototype
Target.prototype = new Temp()
Target.prototype.constructor = Target //为了让Target原型的constructor指回Target
Target.prototype.uber = Origin.prototype //找到自己的超类
}
})()
- 由于es6有了Object.create()方法,可以改变函数的隐式原型(_proto_),也可以不用中间函数
function inherit(Target, Origin) {
Target.prototype = Object.create(Origin.prototype)
Target.prototype.constructor = Target
Target.prototype.uber = Origin.prototype
}
到此,就是继承的圣杯模式的最终写法了!
5.es6继承方式
class Father {
constructor() {
this.age = 18
}
}
class Son extends Father {
constructor() {
super()
}
}