1、原型链继承
构造函数、原型和实例之间的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个原型对象的指针。
继承的本质就是复制,即重写原型对象,代之以一个新类型的实例。
缺点:多个实例对引用类型的操作会被篡改。
function Father(){
this.colors = ['Father'] ;
}
function Son(){
}
// 创建Father的实例,并将该实例赋值给Son.prototype
Son.prototype = new Father() ;
let son = new Son() ;
son.colors.push('Son') ; //这样设置 会篡改 被继承者的 colors 属性,类似 浅拷贝一样,这也是原型链继承的缺点
let son2 = new Son() ;
son2.colors // ['Father','Son'] 被继承者给篡改了
2、借用构造函数继承
使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类(不使用原型)
缺点:
- 只能继承父类的实例属性和方法,不能实现继承原型属性和方法
- 无法实现复用,每个子类都有父类实例函数的副本,影响性能
function Father(){
this.info = ['A','B','C'] ;
}
Father.prototype.SayFather = function(){} ;
function Son(){
Father.call(this) ; //核心代码
}
let father = new Father(),
son = new Son() ;
son.SayFather() ; //报错,TypeError: son.SayFather is not a function
son.info.push( 'D') ;
son.info // ['A','B','C','D'] ;
3、组合继承
将 原型链和构造函数继承 组合,实现原型链继承 原型属性和方法,构造函数 继承 实例属性和方法
缺点:
在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法。
function Father(){
this.name = 'Father';
}
Father.prototype.SayFather = function () {
console.log("Father: ",this.name) ;
} ;
function Son() {
Father.call(this) ;
this.name = "Son" ; //属性重构, 不会篡改 父类属性
this.age = 18 ;
}
Son.prototype = new Father() ; //原型链继承 属性 会增加 到2份
Son.prototype.constructor = Son ; //指向自己,
Son.prototype.SayFather = function () { //方法重构, 不会篡改 父类 方法
console.log("Son Reload: ",this.name) ;
} ;
let father = new Father() ,
son = new Son() ;
father.SayFather() ; //Father: Father
son.SayFather() ; //Son Reload: Son
4、 寄生组合事继承 (最成熟方法)
- `Object.create()`: 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
/**
* 原型链继承:子类继承父类
* */
function inheritProto(son, father) {
let prototype = Object.create(father.prototype); //创建对象,父类原型副本
prototype.constructor = son; //增强对象,弥补重写原型失去的默认的constructor
son.prototype = prototype; //指定对象,将创建的对象覆给 子类的原型
}
function Father(name) {
this.name = name;
}
Father.prototype.SayName = function () {
console.log("Father:", this.name);
};
function Son(name, age) {
Father.call(this, name ); //继承父类实例属性 和 方法
this.age = age;
}
inheritProto(Son, Father);
Son.prototype.SayAge = function () {
console.log("Son", this.age ,this.name, );
};
let son = new Son("MrChen", 19) ,
father = new Father("Rainy") ;
son ==
age: 19
name: "MrChen" //继承父类实例属性
__proto__: Father
SayAge: ƒ () //继承子类原型方法
constructor: ƒ Son(name, age)
__proto__:
SayName: ƒ () //继承父类原型方法
constructor: ƒ Father(name)
__proto__: Object
5、混入方法继承多个对象
function Son(){
SuperClass.call( this ) ;
OtherClass.call( this ) ;
}
Son.prototype = Object.create( SuperClass.prototype ) ;
Object.assigin( Son.prototype, OtherClass.prototype) :
Son.prototype.constructor = Son ;
...
Object.assign会把 OtherSuperClass原型上的函数拷贝到 Son原型上,使 Son 的所有实例都可用 OtherSuperClass 的方法。
6、ES6类继承 extends
super()关键词
class MyClass{
constructor( height, width ){
this.height = height ;
this.width = width ;
}
show(){
}
}
class OtherClass extends{
constructor( height, width, name){
super(height, width) ;
this.name = name
}
}
let c = new OtherClass(18,19,'CC') ;
height: 18
name: "CC"
width: 19
__proto__: MyClass
constructor: class OtherClass
__proto__:
constructor: class MyClass
sayHello: ƒ sayHello()
__proto__: Object