您的位置:首页 > Web前端 > JavaScript

【js基础】javascript中几种常见的继承方式。

2017-07-28 21:08 676 查看
如果只采用原型链来继承,会有诸多问题?

如下:

方式一:原型链继承

function supType(){
this.colors=["red","blue","green"];
}
function subType(){}
subType.prototype=new supType();
var instance1=new subType();
instance1.colors.push("black");
alert(instance1.colors);
var instance2=new subType();
alert(instance2.colors);


首先分析原型链组成:

instance1. _ proto _ ——-》subType.prototype (subType的原型对象);

subType.prototype. _ proto _ ——-》 supType.prototype(supType的原型对象);

supType.prototype. _ proto _ ——–》Object.prototype(Object的原型对象);

对象实例instance2和instance1的原型链一致。

所以再执行instance1.colors.push(“black”);是通过原型链找到subType的原型对象,subtype.prototype对象在创建时,包含了一个colors属性,所以再在subtype.prototype对象的colors属性里加入元素”black”。这就修改了subType原型对象中color属性的内容。

当执行alert(instance2.color),沿着原型链找到subType.prototype 对象,发现colors属性,输出和alert(instance1.colors);一样。

所以通过某一个对象实例对原型链中的属性的修改,会影响全部的队象实例。注意:

对象实例的属性重写不会影响原型链上的原型对象属性的值。

比如:

function supType(){
this.say=function(){
alert("可乐味溜溜梅最好吃");
};
}
supType.prototype.name="溜溜梅";
function subType(){}
subType.prototype=new supType();
var mei1=new subType();
mei1.name="情人梅";
alert(mei1.name);//情人梅
mei1.say();//可乐味溜溜梅最好吃
var mei2=new subType();
alert(mei2.name);//溜溜梅
delete mei1.name;
alert(mei1.name);//溜溜梅


对象实例属性定义和原型链上的某对象属性一致时,会屏蔽,但不会沿着原型链找到该属性,重写该属性值。

由上面分析可知,单一用原型链继承,容易通过对原型链上的某个属性修改,从而影响全部对象实例的初始化。

方式二:借用构造函数

function superType(){
this.colors=["red","blue","green"];
}
function subType(){
superType.call(this);
}
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


这种继承消除了上面原型链那种弊端。

分析过程不难发现,其实这种模式,主要是利用call 和 this 的组合应用的技巧。*当执行var instance1=new subType();语句时,可以理解为:先分配一块内存,变量instance1指向这一块内存,然后再调用subType构造函数,在调用过程中,subType构造函数中this的指向和新创建的对象实例instance1的指向一样,在执行构造函数subType里面的superType.call(this);语句时,由上分析,这里面的this值就是instance1保存的值,然后利用call的特性,让其调用函数中this的值和call方法里面的参数的值一样。所以,又让构造函数supType里面this的指向和call(this)中的this指向一样。简而言之,var instance1=new subType();superType.call(this);this.colors=[“red”,”blue”,”green”];中的instance1,两个this,他们三个指向同一块内存,三个值相等。

所以在执行instance1.color.push(“black”);条语句时,colors属性时直接在instance1对象实例中找到的,不是在原型链某处找到的。所以在push一个”black”时,是直接加到instance1对象实例中的。对原型链中任何一个属性没用影响。

方式三:原型链+借用构造函数(组合继承)

这种继承方式使用的最多的。既吸收了原型链的公共属性继承优势,减少了代码的重复性,又吸收了借用构造函数的修改属性值互不影响的长处,提高了代码的灵活性。

function supType(){
this.kinds=["溜溜梅","蓝莓","情人梅","杨梅"];
}
function subType(){
supType.call(this);//借用构造函数实现继承
}
subType.prototype=new supType();//原型链实现继承
subType.prototype.say=function(){
alert("蓝莓很好吃");
};
var mei1=new subType();
mei1.kinds.pop();
alert(mei1.kinds);//溜溜梅,蓝莓,情人梅
mei1.say();//蓝莓很好吃
var mei2=new subType();
alert(mei2.kinds);//溜溜梅,蓝莓,情人梅,杨梅
mei2.say();//蓝莓很好吃
aler(mei1.say===mei2.say);//true


方式四:原型式继承

直接看代码,都是原型链分析

function object(o){
function F(){};
F.prototype=o;
return new F();
}
var Plum={
names:["溜溜梅","蓝莓","情人梅","杨梅"],
kinds:4
};
var mei1=object(Plum);
mei1.price=35;
mei1.names.push("话梅");
mei1.kinds+=1;
alert(mei1.names);//溜溜梅,蓝莓,情人梅,杨梅,话梅
alert(mei1.kinds);//5
var mei2=object(Plum);
alert(mei2.price);//undefined
alert(mei2.names);//溜溜梅,蓝莓,情人梅,杨梅,话梅
alert(mei2.kinds);//4
alert(mei1.prototype===mei2.prototype);//true


过程分析:对象实例mei1,mei2指向不同,假设mei1的执向foo1,mei2指向foo2,但foo1.prototype==foo2.prototype,即alert(mei1.prototype===mei2.prototype);//true

其实,原型式模式和创建对象时构造函数模式+原型模式的组合基本一致呀。相比组合继承,原型式继承更简单一点。

原型继承的优点就是根据现有的一个对象,可以快速迭代出别的相似对象。不必要再创建设计别的构造函数来创建新对象。

es5中提供的原型函数的API Object.create()来规范原型式继承。

var Plum={
names:["溜溜梅","蓝莓","情人梅","杨梅"],
kinds:4
};
var mei1=Object.create(Plum);
mei1.names.push("话梅");
mei1.kinds+=1;
alert(mei1.names);//溜溜梅,蓝莓,情人梅,杨梅,话梅
alert(mei1.kinds);//5
var mei2=Object.create(Plum);
alert(mei2.names);//溜溜梅,蓝莓,情人梅,杨梅,话梅
alert(mei2.kinds);//4


哎!es5还没学到,es6早已经出来了。呜呜呜……

路漫漫其修远兮 吾将上下而求索。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: