ES5常用七种继承方案和ES6的类继承,有八种继承方案。
ES5:
原型链继承、借用构造函数继承、组合继承、
原型式继承、寄生式继承、寄生组合式继承、
混入方式继承多个对象
ES6:
类继承extends
1.原型链继承
原型链继承就是SuperType的原型指向SuperType的实例,那么就可以继承SuperType原型链上方法。
继承的本质就是复制,即重写原型对象,代之以一个新类型的实例。
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subproperty = false;
}
// 这里是关键,创建SuperType的实例,并将该实例赋值给SubType.prototype
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
return this.subproperty;
}
var instance = new SubType();
console.log(instance.getSuperValue()); // true
原型链方案存在的缺点:多个实例对引用类型的操作会被篡改。
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){}
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green,black"
2.借用构造函数继承
使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类(不使用原型)
function SuperType(){
this.color=["red","green","blue"];
}
function SubType(){
//继承自SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.color.push("black");
alert(instance1.color);//"red,green,blue,black"
var instance2 = new SubType();
alert(instance2.color);//"red,green,blue"
核心代码是SuperType.call(this),创建子类实例时调用SuperType构造函数,于是SubType的每个实例都会将SuperType中的属性复制一份。
缺点:
1.只能继承父类的实例属性和方法,不能继承原型属性/方法
2.无法实现复用,每个子类都有父类实例函数的副本,影响性能
下面我们介绍下SuperType.call(this)的意思
我们需要了解:
1.函数的调用模式
2.this
函数的调用模式
1.
var name = 'qianlong';
var showName = function () {
console.log(this.name);
console.log(this === window);
};
showName(); //qianlong true
2.
var name = 'qianlong';
var showName = function () {
console.log(this.name);
console.log(this === window);
};
showName.call(); //qianlong true
3.
var name = 'qianlong';
var showName = function () {
console.log(this.name);
console.log(this === window);
};
showName.call(undefined); //qianlong true
showName.call(null); //qianlong true
showName.call({}); //undefined false
得出结论:
普通的函数调用和showName.call()形式得到结果都是一样的,函数内的this指向的是window对象(当然这是在浏览器以及非严格模式下),如果而当第一个参数不为空或者undefined以及null的时候,这个时候函数内部的this就是指向传进来的第一个参数。
new中this指的是什么呢
var Person = function () {
this.name = 'qianlong';
this.sex = 'boy';
console.log(this);
};
var p1 = new Person();
你发现了啥? 使用new来调用函数的时候,内部的this就是指向,最终生成的那个对象.
此时,我们再看代码:
function SuperType () {
this.colors = ['red', 'blue', 'green'];
}
function SubType () {
SuperType.call(this); // 这里的this就是指向new SubType() 后生成的对象。
}
var instance1 = new SubType();
// SubType中的this这个时候可以理解为instance1共同指向的一个对象的引用
{
colors: [
'red',
'blue',
'green'
]
}
call 方法就是把目标方法绑定参数对象去执行。SuperType.call(this) 注意这里的this就是SubType实例,此时也就是相当于执行 SubType实例.SuperType().
参考文章:https://segmentfault.com/q/1010000007648841
3.组合继承
组合上述两种方法就是组合继承。用原型链实现对原型属性和方法的继承,用借用构造函数技术来实现实例属性的继承。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
// 继承属性
// 第二次调用SuperType()
SuperType.call(this, name);
this.age = age;
}
// 继承方法
// 构建原型链
// 第一次调用SuperType()
SubType.prototype = new SuperType();
// 重写SubType.prototype的constructor属性,指向自己的构造函数SubType
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
缺点:
1.第一次调用SuperType():给SubType.prototype写入两个属性name,color。
2.第二次调用SuperType():给instance1写入两个属性name,color。
实例对象instance1上的两个属性就屏蔽了其原型对象SubType.prototype的两个同名属性。所以,组合模式的缺点就是在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法。
4.
5.
6.
7.
8.ES6类继承extends
extends关键字主要用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类。其中constructor表示构造函数,一个类中只能有一个构造函数,有多个会报出SyntaxError错误,如果没有显式指定构造方法,则会添加默认的 constructor方法,使用例子如下。
class Rectangle {
// constructor
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea()
}
// Method
calcArea() {
return this.height * this.width;
}
}
const rectangle = new Rectangle(10, 20);
console.log(rectangle.area);
// 输出 200
-----------------------------------------------------------------
// 继承
class Square extends Rectangle {
constructor(length) {
super(length, length);
// 如果子类中存在构造函数,则需要在使用“this”之前首先调用 super()。
this.name = 'Square';
}
get area() {
return this.height * this.width;
}
}
const square = new Square(10);
console.log(square.area);
// 输出 100
extends继承的核心代码如下,其实现和上述的寄生组合式继承方式一样
function _inherits(subType, superType) {
// 创建对象,创建父类原型的一个副本
// 增强对象,弥补因重写原型而失去的默认的constructor 属性
// 指定对象,将新创建的对象赋值给子类的原型
subType.prototype = Object.create(superType && superType.prototype, {
constructor: {
value: subType,
enumerable: false,
writable: true,
configurable: true
}
});
if (superType) {
Object.setPrototypeOf
? Object.setPrototypeOf(subType, superType)
: subType.__proto__ = superType;
}
}
更多详细可以参考文章:
https://juejin.im/post/5bcb2e295188255c55472db0
https://www.cnblogs.com/annika/p/9073572.html
https://segmentfault.com/a/1190000003973606
https://segmentfault.com/a/1190000015727237#articleHeader10
https://segmentfault.com/a/1190000015216289
https://segmentfault.com/a/1190000014476341
http://keenwon.com/1524.html
https://github.com/noahlam/articles/blob/master/JS中的原型对象.md
https://www.jianshu.com/p/342966fdf816
https://juejin.im/entry/5bbf51015188255c3a2d6965
https://juejin.im/post/5bd7f8ed5188252a784d2201#comment
https://zhuanlan.zhihu.com/p/32502909