继承
继承是面向对象编程中讨论最多的话题。很多面向对象语言都支持两种继承:接口继承和实现继承。前者只继承方法签名,后者继承实际的方法。接口继承在ECMAScript中是不可能的,因为函数没有签名。实现继承是ECMAScript唯一支持的继承方式,而这主要是通过原型链实现的。1.原型链
ECMA-262 (有没有史迪仔那个感觉,实验品626) 把原型链定义为ECMAScript的主要继承方式。其基本思想就是通过原型链继承多个引用类型的属性和方法。重温一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。
如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应的另一个原型也有一个指针指向另一个构造函数。这样就在实例和原型之间构造了一条原型链。👇
这里定义了两个类型,SuperType和SubType:
SuperType:
属性👇
function SuperType(){
this.property = true;
}
方法👇
SuperType.prototype.getSuperValue = function(){
return this.property;
}
SubType:
属性👇
function SubType(){
this.subproperty = false;
}
方法👇 这里SubType的原型为SuperType的实例,重新赋值自己的SubType.prototype实现继承。所以SuperType可以访问的的所有属性和方法也会存在于Subtype.prototype。随后有给SubType.prototype创建了一个新的方法。
//继承SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
return this.subproperty;
}
其中,SubType的实例instance可以指向SubType.prototype再指向SuperType.prototype获得getSuperValue()的方法,这个方法和SubType.prototype一样,都是祖先的方法,并不会存储在实例instance上,但instance会继承祖先们的属性,如subproperty。继承的实例和类型的创建者(constructor)都指向最上面的祖先SuperType。
1.默认类型
原型链上还有一环,默认情况下,所有引用类型都继承自Object,这也是通过原型链实现的。任何函数的默认原型都是Object的实例,也就是说,实例有一个内部指针指向Object.prototype,这就是为什么默认对象都有toString()、valueOf()在内所有默认方法的原因。
2.原型与继承关系
instanceof:看👇
为什么这里有一个false我也不知道为什么,我去问大佬啦,知道答案就写上来。
isPrototypeOf():原型链中每个原型都可以调用这个方法。
console.log(Object.prototype.isPrototypeOf(instance)); //true
3.关于方法
子类有时候需要覆盖父类的方法,或者增加父类没有的方法。为此,这些方法必须在原型赋值之后再添加到原型上。
就是直接给原型添加方法就好了。
以字面量方式创建原型方法会破坏之前的原型链,因为相当于重写了原型链。
SubType.prototype = {
nononono;
}
SubType.prototype = fucntion(){
yeahyeahyeah;
}
4.原型链问题
在使用原型实现继承时,原型实际上变成了另一个类型的实例,他们的属性会被积压在其他实例之上。
原型对象:[1]
实例1:push 2 [1,2]
实例2:console.log [1,2]
子类型在实例化时不能给夫类型的构造函数传参。所以一般原型链基本不会单独使用。
2.盗用构造函数
为了解决原型包含引用值导致的继承问题,一种叫做“盗用构造函数”的技术在开发社区流行起来(“对象伪装”“经典继承”)。在子类构造函数中调用父类构造函数。因为毕竟函数就是特定上下文中执行代码的简单对象,所以可以使用apply()和call()方法以新创建的对象为上下文执行构造函数。
function SuperType(){
this.color = ['red','blue']
}
function SubType(){
//继承SuperType
SuperType.call(this);
}
let instance1 = new SubType();
instance1.color.push('black');
console.log(instance1.color);
let instance2 = new SubType();
console.log(instance2.color);
1.传递参数
相比于使用原型链,盗用构造函数的一个优点就是可以在子类构造函数中向父类构造函数传参。
2.盗用构造函数的缺点
盗用构造函数的主要缺点,也是使用构造函数模式自定义类型的问题:必须在构造函数中定义方法,因此函数不能重用。此外,子类也不能使用父类原型上定义的方法,因此所有类型只能使用构造函数模式。
3.组合继承
组合继承综合了前两种办法的优点,使用原型链来继承原型上的属性和方法,而通过盗用构造函数来继承实例属性。
4.原型式继承
5.寄生式继承
寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象。基本的寄生继承模式如下👇
function createAnother(original){
let clone = object(original); //通过函数调用来创建一个新对象
clone.sayHi = function(){ //以某种方式增强这个函数
console.log('hi');
};
return clone; //返回这个函数
}
通过寄生式继承给对象添加函数会导致函数难以重用,与构造函数模式类似。
6.寄生式组合继承
文章内容参考:JavaScript高级程序设计(第四版)标题样式来自:juejin.cn/post/684490…
哪里写错了就联系我🐧:54269504