ECMAscript中继承只支持实现继承,主要是依靠原型链继承来实现的,其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例呢都包含一个指向原型的内部指针。
ECMAscript中的继承就是让一个原型对象等于另一个类型的实例,此时原型对象将包含一个指向另一个原型的指针,相应的,另一个原型也包含一个指向另一个构造函数的指针。相应的,另一个原型中也包含一个指向另一个构造函数的指针,加入另一个原型又是另一个类型的实例,如此层层递进,就构成了实例与原型的链条.
function SuperType(){
this.SuperValue=true;
}
SuperType.prototype.getSuperValue=function(){
return this.SuperValue
}
function SubType(){
this.SubValue=false;
}
SubType.prototype=new SuperType();
SubType.prototype.getSubValue=function(){
return this.SubValue;
}
var exp=new SubType();
console.log(exp.getSuperValue());//true
在这个继承中,要注意几个关系,exp指向了SubType的原型,SubType指向了SuperType的原型,getSuperValue()存在与SuperType.prototype中,但是SuperValue却存在于SubType.prototype中,在我看来,继承是让一个原型作为另一个原型的实例。需要注意的是exp.constructor现在指向的是SuperType,这是因为SubType.prototype中的constructor被改写的缘故。编写时需要考虑到Object这个默认的原型
继承中方法重写需要注意,当我们要重写或者添加超类中不存在的某个方法时,这些代码一定要放在继承之后。同时还不能使用字面量创建原型方法,因为这样会改变constructor的值,从而改变原型链
原型链继承也并非没有问题,如同原型模式一样,若是包含引用类型值的原型,会被所有实例共享,而这也正是为什么要在构造函数中,而不是原型对象中定义舒心的原因,在通过原型来实现继承的时候,原型实际上会变成另一个原型的实例。于是原先的实例属性也就顺理成章的变成了现在的原型属性。
function SuperType(){
this.color=["red","orange","green"];
}
function SubType(){
}
SubType.prototype=new SuperType();
var ins1=new SubType();
ins1.color.push("pink");
console.log(ins1.color)//["red", "orange", "green", "pink"]
var ins2=new SubType();
console.log(ins2.color)//["red", "orange", "green", "pink"]
在这个例子中,我们对ins1的操作同样也体现在了ins2上,这是因为本来color是SuperType上的属性,SubType通过继承SuperType,在他的proto属性上添加了一个color属性,等于就是创建了一个SubType.prototype.color属性,这样color属性就变成了原型属性,被ins1和ins2共享了。
借用构造函数
这种方法主要思想就是在子类构造函数内部使用call或者apply来调用超类构造函数。
function SuperType(){
this.color=["yellow","red","green"];
}
function SubType(){
SuperType.call(this);
}
var exp=new SubType();
var exp2=new SubType();
exp.color.push("black");
console.log(exp.color)// ["yellow", "red", "green", "black"]
console.log(exp2.color)// ["yellow", "red", "green"]
这种方法没有原型对象的使用,也就不存在指针的改变,每个实例都会具有自己的属性副本,我们通过这种方式还能向超类传递参数,我们知道call和apply可以接收好几个参数,通过这种方式,我们就可以向超类传递参数
function SuperType(name){
this.name=name;
}
function SubType(){
SuperType.call(this,"burning");
}
var exp=new SubType();
console.log(exp.name)//burning
如果仅仅是借用构造函数,那么也无法避免构造函数模式存在的问题—方法都在构造函数中定义,因此函数复用就无从谈起。而且在超类的原型中定义的方法,对于子类而言是不可见的,结果所有类型都只能使用构造函数模式。考虑到这些问题,借用构造函数的技术也很少单独使用。
组合式继承
指的是将原型链和借用构造函数的技术拼合到一起,使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。即通过在原型上定义方法实现了函数的复用,又保证每个实例都有他的属性。
function SuperType(name){
this.name=name;
this.color=["yellow","red","green"];
}
SuperType.prototype.sayName=function(){
console.log(this.name)
}
function SubType(name,age){
SuperType.call(this,name);//属性在这里继承
this.age=age;
}
SubType.prototype=new SuperType();//方法在这里继承
SubType.prototype.constructor=SubType;//修正constructor属性的指向,ES6的class语法已经修复这个bug
SubType.prototype.sayAge=function(){
console.log(this.age)
}
var exp1=new SubType("burning",28);
exp1.color.push("black");
exp1.sayName();//burning
exp1.sayAge();//28
console.log(exp1.color)//["yellow", "red", "green", "black"]
var exp2=new SubType();
console.log(exp2.color)//["yellow", "red", "green"]
原型式继承
function object(o){
function F(){};
F.prototype=o;
return new F();
}
这种方法是要求我们传入一个对象作为临时的构造函数对象作为他的原型,最后返回了一个临时类型的新实例。从本质上将,object()对传入的对象进行了一次潜复制。
var person={
name:"burning",
friend:["ROTK","ZSMJ"]
}
var anotherMan=Object.create(person);
anotherMan.name="Gray";
anotherMan.friend.push("Maybe");
var yetAnotherMan=Object.create(person);
yetAnotherMan.name="OldChicken";
yetAnotherMan.friend.push("chuan");
console.log(person.friend);//["ROTK", "ZSMJ", "Maybe", "chuan"]
console.log(anotherMan.friend)//["ROTK", "ZSMJ", "Maybe", "chuan"]
console.log(yetAnotherMan.friend)//["ROTK", "ZSMJ", "Maybe", "chuan"]
这个新创建的对象将以person为原型,所以他的原型中就包含一个引用类型的值,friend属性就被共享了
是第二个参数,这个参数与Object.defineProperties()的参数相同,通过自己的描述符定义的,这种方法可以覆盖原型对象上的同名属性,也就是方法重写。
var person={
name:"burning",
friend:["ROTK","ZSMJ","Maybe"]
}
var anotherMan=Object.create(person,{
name:{
value:"jack"
}
})
console.log(anotherMan.name);//jack
寄生式继承
这种模式如同寄生构造函数和工厂模式类似,创建一个用于封装继承过程的函数
function create(origial){
var clone=Object(origial);
clone.sayHi=function(){
alert("hi");
}
return clone;
}
var person={
name:"jack",
friend:["rose","mike"]
}
var another=create(person);
another.sayHi();//hi
本文深入探讨了ECMAScript中的各种继承模式,包括原型链继承、借用构造函数、组合式继承、原型式继承和寄生式继承。通过具体示例展示了每种继承方式的特点及其可能遇到的问题。

被折叠的 条评论
为什么被折叠?



