继承有很多种方法,不过这都是在原型链的基础上演变的,理解了原型链之后,对于继承就没有什么太大的难题了。与其说这是继承,不如说是委托代理,意为在对象上找不到的属性或方法,就去对象关联的原型对象上查找,逐级委托,直到Object.prototype。这里为了跟其他文章统一说明,故而沿用继承的说法。
最基本的便是原型链继承,通过手动篡改对象的原型对象,即可实现继承。
function Father(name){
this.name = name
}
Father.prototype.sayName=function (){console.log(this.name)}
//定义一个父类构造函数
function Child (sex,name){
this.sex = sex
Father.call(this,name)
}
Child.prototype = new Father()
Child.prototype.constructor = Child
//定义Child的原型指向,同时将Child原型对象中的构造函数指向Child本身
var test = new Child('Tome',1)
//console.log(test)可以发现test的原型链当中确实是指向了Father,test instanceof Father的结果也为true,但是这样会有一个弊端,就是在原型对象中会有一个被遮蔽的name属性,这是因为每次创建一个Child实例都会执行两遍Father函数。
在此基础上,衍生出了寄生组合继承,原理是创建一个中间函数,利用这个函数来避免上述问题。
function Father(name){
this.name = name
}
Father.prototype.sayName=function (){console.log(this.name)}
//定义一个父类构造函数
function Child (name,sex){
this.sex = sex
Father.call(this,name)
}
//定义一个子类
function fix(childObj,fatherObj){
var middleObj = Object.create(fatherObj.prototype)
childObj.prototype = middleObj
middleObj.constructor = childObj
}
var a= new Child('Jerry',1)
console.log(a)
//重写Child的prototype,这样就避免了调用两次Father,在test的原型链中,也不会有被遮蔽的name属性。
//本质上是在重写Child的prototype和constructor。
再来说说ES6中的类继承,这其实跟ES5中的寄生组合原理是一样的,只不过ES6让语法变得优雅起来。
class Father {
constructor(name) {
this.name = name
}
sayName () {
console.log(this.name);
}
}
class Child extends Father {
constructor(name, sex) {
super(name)
this.sex = sex
}
}
var a = new Child('Jerry', 1)
console.log(a)
//打印出来可以看到,ES6新语法中的a跟ES5中的寄生组合中的a是一样的。