构造函数继承父类属性
核心原理:通过call()把父类型的this指向子类型的this,可实现属性继承
function Father(name,age) {
this.name = name; //this指向的是父构造函数的对象实例
this.age = age;
}
//子构造函数
function Son(name,age,score) {
//使父构造函数中的this指向子构造函数中的this
Father.call(this,name,age);
//还可以添加自己的属性
this.score = score;
}
var son = new Son('刘德华',45,99);
console.log(son); //{name: "刘德华", age: 45, score: 99}
优点:解决了superType中的私有属性变公有的问题,可以传递参数
缺点:
1.实例并不是父类的实例,只是子类的实例
2.只能继承父类的实例属性和方法,不能继承原型属性和方法
原型链继承
一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法。
1. 将子类所共享的方法提取出来,让子类的 prototype 原型对象 = new 父类()
2. 本质:子类原型对象等于是实例化父类,因为父类实例化之后另外开辟空间,就不会影响原来父类原型对象
3. 将子类的 constructor 从新指向子类的构造函数

⚠️:使用构造函数的prototype指向父对象的一个实例,需要注意把子构造函数的constructor构造器重新指向自己,因为使用prototype指向了父构造函数的prototype,会出现原型链紊乱
function Father() {
this.name = 'zhang san'
}
Parent.prototype.getName = function() {
console.log(this.name)
}
function Son() {
}
Son.prototype = new Father()
var son1 = new Son()
son1.getName() // zhang san
优点:能通过instanceOf和isPrototypeOf的检测
缺点:由于原型链继承共享实例属性的缺点,如果是属于引用类型传值,引用副本实例属性的修改必然会引起其他副本实例属性的修改,所以不常使用;
另一个缺点在于不能向父类构造函数随时传递参数,很不灵活
组合继承:原型链继承+构造函数继承
通俗来讲就是用原型链实现对原型属性和方法的继承,用借用构造函数继承来实现对实例属性的继承。
function Father(name,age) {
this.name = name; //this指向的是父构造函数的对象实例
this.age = age;
}
Father.prototype.money = function () {
console.log(10000);
}
//子构造函数
function Son(name,age,score) {
//使父构造函数中的this指向子构造函数中的this
Father.call(this,name,age); //借用构造函数, 第一次调用父类构造函数
//还可以添加自己的属性
this.score = score;
}
//把father的实例话对象赋值给Son的原型对象
Son.prototype = new Father(); // 原型链继承, 第二次调用父类构造函数
//修改了子类的原型对象指向,还要利用constructor指向原来的构造函数
Son.prototype.constructor = Son;
var son = new Son('刘德华',44,89);
son.money(); //10000
console.log(Son.prototype); //Father
组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为 JavaScript 中最常用的继承模式。而且,instanceof 和 isPrototypeOf()也能够用于识别基于组合继承创建的对象。
缺点: 调用了两次父类构造函数,生成了两份实例,一个子类实例,一个父类实例,父类实例作为prototype使用