JavaScript中的继承

240 阅读2分钟

继承有很多种方法,不过这都是在原型链的基础上演变的,理解了原型链之后,对于继承就没有什么太大的难题了。与其说这是继承,不如说是委托代理,意为在对象上找不到的属性或方法,就去对象关联的原型对象上查找,逐级委托,直到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是一样的。