(序语:本人写得不是很好,如果有什么错误请指出或者联系我e-mail:kenlist@163.com)
Javascript中的继承方式主要有3种.之所以会有多种继承方式,是因为ECMAScript制造者当初没有真正为继承定义一套完整的规定.事实上,这里的继承方式都是开发者们通过努力获得的.
第一种(call继承法):
使用call关键字,是调用函数(function对象)的函数.
例如:
function
sayColor(sPrefix,sSuffix)
...
{document.write(sPrefix + this.color + sSuffix);}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
var
obj
=
new
Object();
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
obj.color
=
"
red
"
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
sayColor.call(obj,
"
the color is
"
,
"
,a very nice coor indeed.
"
);
这里输出了the color is red,a very nice coor indeed.从而可以知道它能把内部的this指针指向输入对象(即obj).
深入了解call,使我们一会儿对继承使用有更好了解.现在我们再看看下面的代码:
function
sayColor(sPrefix,sSuffix)
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
document.write(sPrefix + this.color + sSuffix);
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
this.showgood = function()...{document.write("hello.");};
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
this.color = "blue";
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
var
obj
=
new
Object();
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
obj.color
=
"
red
"
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
sayColor.call(obj,
"
the color is
"
,
"
,a very nice color indeed.
"
);
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
obj.showgood();
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
document.write(obj.color);
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
输出:this color is red,a very nice color indeed.hello.blue
由输出见,因为sayColor内部的this指针通过call函数调用时,指向了obj,所以obj就绑定了showgood()和color.而因为obj原来就有color一值,当调用call时,此值被重新赋予指针,即this.color覆盖原来的obj.color,所以值发生改变.由此可见,call函数并不单纯地调用函数,它还能把内部的this指向我们输入的对象.从而,聪明的开发者们想到了用它来作继承.(这种方法又称对象冒充法)下面代码为继承代码:
function
ClassA(sColor)
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
this.color = sColor;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
this.showColor = function()...{ document.write(this.color);};
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
function
ClassB(sColor,sName)
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
ClassA.call(this,sColor); //这里就使用call方法,使ClassA的属性方法绑定到这里.
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
this.name = sName;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
this.showname = function()...{document.write(sName))};
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
var
objA
=
new
ClassA(
"
red
"
);
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
objA.showColor();
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
var
objB
=
new
ClassB(
"
blue
"
,
"
kenlist
"
);
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
objB.showColor();
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
objB.showName();
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
document.write(objB
instanceof
ClassA);
//
返回false
从输出可以知道,的确成功地继承了并重新赋值了.并且在最后我家了一句instanceof的比较语句, 也成功地返回了false.
这种继承方法有个优点,就是能执行多继承,可以同时绑定多个类.但由于call函数的特性,最下面的类的方法总是拥有优先级.
这种继承方式也有个缺点,就是不能继承原型模版的方法与属性,例如如果把ClassA中的this.color改为ClassA.prototype.color则会输出undefined.为什么会这样?因为call是把ClassB对象给予ClassA去进行指针定向,由前面知识可以知道,如果不用this的话,是没办法让指针指到ClassB类上的,所以prototype只是ClassA的模版,并不能使里面的方法属性指向ClassB.但由前面类的知识可知,我们创建类时,虽然要原型来定义方法,这样才能使类不重复分配一个空间.很显然,这种继承方法存在这种缺陷.下面将讲解第二种方法去继承,这种方法从一定程度上祢补了这个缺陷.
第二种(原型链继承):
先看代码:
function
ClassA()
...
{
ClassA.prototype.color = "red";
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
ClassA.prototype.sayColor = function()...{
document.write("my color is:" + this.color + "<br/>");
};
}
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
function
ClassB()
...
{
this.name = "kenlist";
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
ClassB.prototype.sayName = function()...{
document.write("my name is:" + this.name);
};
}
//
使用原型链覆盖到ClassB的原形模版上.
//
不能使用构造函数.因为这里的new ClassA()括号内不允许有任何东西,这是它的定义.
ClassB.prototype
=
new
ClassA();
var
objA
=
new
ClassA();
var
objB
=
new
ClassB();
objA.sayColor();
objB.sayColor();
//
因为模版复制问题,这里会返回true
document.write(objB
instanceof
ClassA);
输出是正常地输出,从代码中可以看出,关键部分就是使用了ClassB.prototype = new ClassA()这里,这里的意思为把ClassA的原型模版先用new语句复制一个新的出来,然后把ClassB的原型模版指针指向复制品.从而导致ClassB具有了ClassA的模版.这个语句不能放在建立类的内部,因为在function Classb()内创建的只能是基于一个特定的模版进行修改,而不能在里面重新定义原型模版.所以必须在外面先建立原型模版.然后再在ClassB里面继续添加属性与方法.
这种方法缺点非常多,我们一条条地列出:
1)不能使用构造函数,在其中一条蓝色字的注释已经说明,当让ClassB的原型链指向ClassA的原型链时,不能在ClassA()的括号中添加任何参数,这导致了不能为ClassA输入参书,从而导致缺少了构造函数.
2)基类一定要用原型模版定义所有方法和属性,否则没法继承.因为我们定义类时,属性通常是绑定到该对象上,所以使用this指针,然而这里如果使用this指针则没办法由原型模版实现继承.
3)在橙色字的注释中可以知道,objB instanceof Classa竟然是返回true,事实上,instanceof语句检测的是原型模版的指向性,因为ClassB实际上是指向ClassA的模版,所以instanceof语句就分不清ClassB和ClassA了.
4)不能实现多继承,由于原型模版的重定向会使原来的模版丢失,所以不能实现多继承.
基于以上2种方法的缺陷,下面的第三种方法,则能则是参照类的做法.
第三种(混合对象冒充/原型链继承)
由前面2种方法知道可以通过call使this绑定到子类上,通过原型链继承,可以把原型模版复制到子类.这两种方法结合,就能使混合方式的类得到较好的继承.看看下面代码:
function
ClassA(sColor)
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
this.color = sColor; //属性使用对象绑定
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
ClassA.prototype.showColor = function()...{ document.write(this.color); }; //方法使用原型模版法
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
function
ClassB(sColor,sName)
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
ClassA.call(this,sColor); //使属性绑定到该类
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
this.name = sName;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
ClassB.prototype.showName = function()...{ document.write(this.name);};
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
ClassB.prototype
=
new
ClassA();
//
使用原型链方法使复制模版
var
objA
=
new
ClassA(
"
red
"
);
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
objA.showColor();
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
var
objB
=
new
ClassB(
"
blue
"
,
"
kenlist
"
);
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
objB.showColor(); objB.showName();
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
输出结果很正常,而且也实现了属性使用绑定,方法使用原型模版.这种方法也祢补了上面2种继承方法的缺陷.从而达到较好的继承效果.但这种方法也是有缺陷,因为使用原型模版重定向方法继承,从而导致不能多继承.而且因为原形模版重定向不能写在类内部,也不能是用动态原型方法,从而浪费了比小内存.荣幸的是,聪明的开发者们开发了个小库来帮助我们完成这些任务,祢补这些缺失.
第四种(zinherit继承)
这种方法是最有效,最规范,缺失最小的一种,但它需要从网上下载一个小库zinherit.js(http://www.nczonline.net/downloads处下载)这个小库能有效解决多重继承和动态原型问题.看看下面代码:
function
ClassA(sColor)
...
{
this.color = sColor;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(typeof ClassA._initialized == "undefined")...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
ClassA.prototype.showColor = function()...{
document.write(this.color + "<br/>");
} ;
ClassA._initialized = true;
};
}
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
function
ClassB(sName)
...
{
this.name = sName;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(typeof ClassB._initialized == "undefined")...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
ClassB.prototype.showName = function()...{
document.write(this.name + "<br/>");
};
ClassB._initialized = true;
};
}
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
function
ClassC(sColor,sName,sSize)
...
{
this.size = sSize;
//先使用call方法是属性绑定
ClassA.call(this,sColor);
ClassB.call(this,sName);
//下面使用小库zinherit.js的原型模版继承方法
//这种继承方法使原型模版重定向能在类内部完成.
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(typeof ClassC._initialized == "undefined")...{
ClassC.prototype.inheritFrom(ClassA);
ClassC.prototype.inheritFrom(ClassB);
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
ClassC.prototype.showSize = function()...{
document.write(this.size + "<br/>");
};
ClassC._initialized = true;
};
}
document.write(
"
-----ClassA-----<br/>
"
);
var
objA
=
new
ClassA(
"
red
"
);
objA.showColor();
document.write(
"
-----ClassB-----<br/>
"
);
var
objB
=
new
ClassB(
"
superman
"
);
objB.showName();
document.write(
"
-----ClassC-----<br/>
"
);
var
objC
=
new
ClassC(
"
orange
"
,
"
kenlist
"
,
100
);
objC.showColor();
objC.showName();
objC.showSize();
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
输出:
-----ClassA-----
red
-----ClassB-----
superman
-----ClassC-----
orange
kenlist
100
由输出可以得出,这种建立类的方法非常符合标准,只需要在类中加入继承语句:ClassC.prototype.inheritForm(ClassB),就能使ClassC的原型模版得到重定向并且不用定义在类外面.还能实现多继承,动态原型.
分析了上面4种方法(call继承、原型链继承、混合对象冒充/原型链继承、zinherit继承),我觉得第4种方法比较有效,但要引入库而且通过库处理会使执行效率降低,所以如果不需要多继承,建议使用混合对象冒充/原型链继承方法。但如果对执行速率要求不是太高或者要求多继承,则建议使用zinherit继承方法,因为这种方法既能规范化,又能实现多继承。
写了这么多,可能存在不小勘误,希望大家多给意见哈~