JavaScript 的继承方式与取舍
来源:互联网 发布:医疗软件销售招聘 编辑:程序博客网 时间:2024/06/05 02:12
(序语:本人写得不是很好,如果有什么错误请指出或者联系我e-mail:kenlist@163.com)
Javascript中的继承方式主要有3种.之所以会有多种继承方式,是因为ECMAScript制造者当初没有真正为继承定义一套完整的规定.事实上,这里的继承方式都是开发者们通过努力获得的.
第一种(call继承法):
使用call关键字,是调用函数(function对象)的函数.
例如:
var obj = new Object();
obj.color = "red";
sayColor.call(obj,"the color is ",",a very nice coor indeed.");
这里输出了the color is red,a very nice coor indeed.从而可以知道它能把内部的this指针指向输入对象(即obj).
深入了解call,使我们一会儿对继承使用有更好了解.现在我们再看看下面的代码:
document.write(sPrefix + this.color + sSuffix);
this.showgood = function()...{document.write("hello.");};
this.color = "blue";
}
var obj = new Object();
obj.color = "red";
sayColor.call(obj,"the color is ",",a very nice color indeed.");
obj.showgood();
document.write(obj.color);
输出: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指向我们输入的对象.从而,聪明的开发者们想到了用它来作继承.(这种方法又称对象冒充法)下面代码为继承代码:
this.color = sColor;
this.showColor = function()...{ document.write(this.color);};
}
function ClassB(sColor,sName)...{
ClassA.call(this,sColor); //这里就使用call方法,使ClassA的属性方法绑定到这里.
this.name = sName;
this.showname = function()...{document.write(sName))};
}
var objA = new ClassA("red");
objA.showColor();
var objB = new ClassB("blue","kenlist");
objB.showColor();
objB.showName();
document.write(objB instanceof ClassA); //返回false
从输出可以知道,的确成功地继承了并重新赋值了.并且在最后我家了一句instanceof的比较语句, 也成功地返回了false.
这种继承方法有个优点,就是能执行多继承,可以同时绑定多个类.但由于call函数的特性,最下面的类的方法总是拥有优先级.
这种继承方式也有个缺点,就是不能继承原型模版的方法与属性,例如如果把ClassA中的this.color改为ClassA.prototype.color则会输出undefined.为什么会这样?因为call是把ClassB对象给予ClassA去进行指针定向,由前面知识可以知道,如果不用this的话,是没办法让指针指到ClassB类上的,所以prototype只是ClassA的模版,并不能使里面的方法属性指向ClassB.但由前面类的知识可知,我们创建类时,虽然要原型来定义方法,这样才能使类不重复分配一个空间.很显然,这种继承方法存在这种缺陷.下面将讲解第二种方法去继承,这种方法从一定程度上祢补了这个缺陷.
第二种(原型链继承):
先看代码:
ClassA.prototype.color = "red";
ClassA.prototype.sayColor = function()...{
document.write("my color is:" + this.color + "<br/>");
};
}
function ClassB()...{
this.name = "kenlist";
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绑定到子类上,通过原型链继承,可以把原型模版复制到子类.这两种方法结合,就能使混合方式的类得到较好的继承.看看下面代码:
this.color = sColor; //属性使用对象绑定
ClassA.prototype.showColor = function()...{ document.write(this.color); }; //方法使用原型模版法
}
function ClassB(sColor,sName)...{
ClassA.call(this,sColor); //使属性绑定到该类
this.name = sName;
ClassB.prototype.showName = function()...{ document.write(this.name);};
}
ClassB.prototype = new ClassA(); //使用原型链方法使复制模版
var objA = new ClassA("red");
objA.showColor();
var objB = new ClassB("blue","kenlist");
objB.showColor(); objB.showName();
输出结果很正常,而且也实现了属性使用绑定,方法使用原型模版.这种方法也祢补了上面2种继承方法的缺陷.从而达到较好的继承效果.但这种方法也是有缺陷,因为使用原型模版重定向方法继承,从而导致不能多继承.而且因为原形模版重定向不能写在类内部,也不能是用动态原型方法,从而浪费了比小内存.荣幸的是,聪明的开发者们开发了个小库来帮助我们完成这些任务,祢补这些缺失.
第四种(zinherit继承)
这种方法是最有效,最规范,缺失最小的一种,但它需要从网上下载一个小库zinherit.js(http://www.nczonline.net/downloads处下载)这个小库能有效解决多重继承和动态原型问题.看看下面代码:
this.color = sColor;
if(typeof ClassA._initialized == "undefined")...{
ClassA.prototype.showColor = function()...{
document.write(this.color + "<br/>");
} ;
ClassA._initialized = true;
};
}
function ClassB(sName)...{
this.name = sName;
if(typeof ClassB._initialized == "undefined")...{
ClassB.prototype.showName = function()...{
document.write(this.name + "<br/>");
};
ClassB._initialized = true;
};
}
function ClassC(sColor,sName,sSize)...{
this.size = sSize;
//先使用call方法是属性绑定
ClassA.call(this,sColor);
ClassB.call(this,sName);
//下面使用小库zinherit.js的原型模版继承方法
//这种继承方法使原型模版重定向能在类内部完成.
if(typeof ClassC._initialized == "undefined")...{
ClassC.prototype.inheritFrom(ClassA);
ClassC.prototype.inheritFrom(ClassB);
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();
输出:
-----ClassA-----
red
-----ClassB-----
superman
-----ClassC-----
orange
kenlist
100
由输出可以得出,这种建立类的方法非常符合标准,只需要在类中加入继承语句:ClassC.prototype.inheritForm(ClassB),就能使ClassC的原型模版得到重定向并且不用定义在类外面.还能实现多继承,动态原型.
分析了上面4种方法(call继承、原型链继承、混合对象冒充/原型链继承、zinherit继承),我觉得第4种方法比较有效,但要引入库而且通过库处理会使执行效率降低,所以如果不需要多继承,建议使用混合对象冒充/原型链继承方法。但如果对执行速率要求不是太高或者要求多继承,则建议使用zinherit继承方法,因为这种方法既能规范化,又能实现多继承。
写了这么多,可能存在不小勘误,希望大家多给意见哈~
- JavaScript 的继承方式与取舍
- javascript的继承方式
- JavaScript继承的方式
- in与exists的取舍
- javascript继承对象的方式
- javascript实现继承的方式
- javascript实现继承的方式
- Javascript的继承方式详解
- 1. javascript继承的方式
- javascript实现继承的方式
- Javascript继承的实现方式
- JavaScript的继承与多继承
- String,StringBuffer与StringBuilder的取舍
- Python 内存与读写时间的取舍
- NUMA的取舍与优化设置
- Ajax和Flash的比较与取舍
- javascript的类与继承
- JavaScript的原型与继承
- 中文转换为完整拼音算法原理分析
- IIS上配置WAP网站的方法
- Javascript的IE和Firefox兼容性汇编[转载]
- 单用户多角色权限的MSSQL实现
- 选择正确的.net技术[翻译]
- JavaScript 的继承方式与取舍
- asp.net1.1中动态更改页面标题
- codeproject上发现的DataSet调试利器
- javascript中手动onchange事件触发
- 一个简单的计算器源代码[VC6]
- (转)Rose与PowerDesigner:两款建模工具的对比
- 基于语言文件的asp.net全球化解决方案
- javascript在netscape浏览器中寻找控件的注意点
- c#科学计算器测试版发布