1、JavaScript 继承方式
(1)对象冒充
在 JavaScript 中,构造函数也和普通的函数一样,可以被赋值和调用,对象冒充通过此原理来模拟继承。
<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <Metacharset="UTF-8"> <title>declare</title> </head> <body> <scripttype="text/javascript"> functionClassA(name) { this.name=name; this.sayHello=function(){ alert("Hello,"+this.name); } } functionClassB(name,time) { this.newMethod=ClassA; this.newMethod(name); deletethis.newMethod; this.time=time; this.sayGoodbye=function(){ alert("Goodbye"+this.name+",it's"+this.time+"now!"); } } varobjA=newClassA("Tom"); varobjB=newClassB("Jerry","11:30am"); console.log("开始执行-------") objA.sayHello();//outputis:"Hello,Tom" objB.sayHello();//outputis:"Hello,Jerry" objB.sayGoodbye();//outputis:"GoodbyeJerry,it’s11:30amnow!" </script> </body> </html>
将 ClassA 的构造函数赋值为 ClassB 的一个普通方法,然后调用它,由于此时 this 指向的是 ClassB 的实例,那么 ClassB 的实例就会收到 ClassA 构造函数中定义的属性和方法,从而达到了继承的效果。
需要注意的是,应及时删除临时引用(this.newMethod),以防止 ClassB 更改 ClassA 类对象的引用。因为对临时引用(this.newMethod)的更改,也会导致 ClassA 的结构变化。并且 ClassB 的所有新属性和新方法,应该在删除临时引用后定义,否则,可能会覆盖父类的相关属性和方法。
可以采用 JavaScript 中的 call 或者 apply 函数达到同样的效果。
(2)基于原型的继承
JavaScript 中的每个对象都包含一个原型对象(prototype),指向对某个对象的引用,而由于原型对象本身也是对象,则也会包含对它的原型的引用,由此构成一条原型链。原型链终止于内建 Object 类的原型。当要读取某个对象的属性或方法时,JavaScript 首先在该对象中查找,若没有找到,便在该对象的原型中继续查找,若仍未找到,便顺着原型链继续在原型的原型中查找,直到查找到或到达原型链的尽头。这样的系统被称为原型继承。而基于原型的继承,则是指利用了 prototype 或者说以某种方式覆盖了 prototype,从而达到属性及方法复用的目的。
<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <Metacharset="UTF-8"> <title>declare</title> </head> <body> <scripttype="text/javascript"> functionClassA() { this.name=""; this.sayHello=function(){ alert("Hello,"+this.name); } } functionClassB(){}; ClassB.prototype=newClassA(); varobjB=newClassB(); objB.name="Jerry"; objB.sayHello();//output:"Hello,Jerry"; </script> </body> </html>
(3)混合方式继承
<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <Metacharset="UTF-8"> <title>declare</title> </head> <body> <scripttype="text/javascript"> functionClassA(name){ this.name=name; } ClassA.prototype.sayHello=function(){ alert("Hello,"+this.name); } functionClassB(name,time){ ClassA.call(this,name); this.time=time; } ClassB.prototype=newClassA(); ClassB.prototype.sayGoodbye=function(){ alert("Goodbye"+this.name+",it's"+this.time+"now!"); } varobjA=newClassA("Tom"); varobjB=newClassB("Jerry","11:30am"); objA.sayHello();//outputis:"Hello,it’s11:30amnow!" </script> </body> </html>
2、dojo.declare
dojo.declare可以声明一个类,而不污染继承的类。
语法:
dojo.declare(/*String*/ className,/*Function | Function[]*/ superclass,/*Object*/ props)
参数说明:
className: 是要要申明的类的类名,也就是创建的构造函数的名称,可忽略。
superclass:所要继承的父类,此参数可为 null,表示没有父类。
props:类体,包括类的属性和方法,由名、值(key,value)对组成,其中constructor 为保留字,此函数用来初始化新对象。
(1)命名类
//Createanewclassnamed"mynamespace.MyClass" declare("mynamespace.MyClass",null,{ //Custompropertiesandmethodshere });
现在在应用程序中,一个叫 mynamespace.MyClass 的类已经全局可用了。一般来说只有需要通过 Dojo parser 解析时才定义命名类。其他情况下,我们定义的类一般都忽略 className 参数。
(2)匿名类
//Createascoped,anonymousclass varMyClass=declare(null,monospace;background-image:none;border:0px;float:none;height:auto;line-height:1.1em;margin:0px;padding:0px;width:auto;font-size:1em;color:#000000;">
现在 MyClass 类仅在给定空间中有效。
(3)定义无继承的类
varMyClass=declare(null,monospace;background-image:none;border:0px;float:none;height:auto;line-height:1.1em;margin:0px;padding:0px;width:auto;font-size:1em;color:#000000;">
(4)定义从已有类继承的类
新的 MySubClass 类会继承 MyClass 类的所有属性和方法。父类方法或属性(key:新值)可以被重载。
(5)定义从多个类继承的类
varMyMultiSubClass=declare([ MySubClass,MyOtherClass,MyMixinClass ],{ //MyMultiSubClass现在拥有以下类的所有属性和方法:: //MySubClass,andMyMixinClass });
这个数组就表明是从多个类继承。属性和方法会按照父类从左到右进行继承。 数组列表中的第一个类将提供基础原型(base prototype),其他的类则会被混入到新类中。
如果父类具有相同的属性或者方法,则最后一个父类的属性或方法会被采纳。
样例:
--Basic Class 的创建和继承
define([ "dojo/_base/declare","dijit/form/Button" ],function(declare,Button){ returndeclare("mynamespace.Button",Button,{ label:"MyButton",onClick:function(evt){ console.log("Iwasclicked!"); this.inherited(arguments); } }); });
上面代码创建一个继承自 dijit/form/Button 的小部件 (widget):
(1)类名是 mynamespace.Button;
(2)可通过全局类名 mynamespace.Button 引用,可以通过模块返回值引用;
(3)类继承自: dijit/form/Button (以及 Button's 的依赖关系);
(6)构造函数constructor
constructor在类派生实例对象时触发,随后在该对象的作用域执行。因此 this 关键字是指向对象实例,而非类。constructor 方法可以接受任意长度参数,用于实例初始化。
//Createanewclass varTwitter=declare(null,{ //Thedefaultusername username:"defaultUser",//Theconstructor constructor:function(args){ declare.safeMixin(this,args); } });
创建一个实例对象:
varmyInstance=newTwitter();
这里没有指定 username,缺省值为 "defaultUser" 。To leverage the safeMixin method,我们可以提供 username 参数
varmyInstance=newTwitter({ username:"sitepen" });
declare.safeMixin 用在类的创建和继承中,是用于创建具有不定参数的类的好帮手。
(7)继承Inheritance
继承关系是通过 declare 的第二个参数定义的。如果某个属性或方法已存在,则子类会按照父类参数,从左至右进行替换混入。
<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <Metacharset="UTF-8"> <title>declare</title> <scripttype="text/javascript"src="dojo/dojo.js"data-dojo-config="async:true"></script> </head> <body> <scripttype="text/javascript"> require(["dojo/_base/declare"],function(declare){ //定义类A varA=declare(null,{ //Afewproperties... propertyA:"Yes",propertyB:2,add:function(a1){ returna1; } }); //定义类B varB=declare(A,{ //Afewproperties... propertyA:"Maybe",propertyB:1,propertyC:true,add:function(a1,a2){ returna1+a2; } }); //定义类C varC=declare([A,B],{ //Afewproperties... propertyA:"No",propertyB:99,propertyD:false,objectVal:[1,2,3] }); //创建实例 varinstance=newC(); console.log("propertyA值:"+instance.propertyA); console.log("propertyB值:"+instance.propertyB); console.log("propertyC值:"+instance.propertyC); console.log("propertyD值:"+instance.propertyD); console.log("add方法:"+instance.add(10,20)); console.log("objectVal值:"+instance.objectVal); instance.propertyA="YES"; instance.objectVal.push(5); varinstance1=newC(); console.log("另一个实例propertyA值:"+instance1.propertyA); console.log("另一个实例objectVal值:"+instance1.objectVal); }); </script> </body> </html>
输出结果:
"propertyA值:No" declare.html:43
"propertyB值:99" declare.html:44
"propertyC值:true" declare.html:45
"propertyD值:false" declare.html:46
"add方法:30" declare.html:47
"objectVal值:1,3" declare.html:48
"另一个实例propertyA值:No" declare.html:52
"另一个实例objectVal值:1,3,5"
当我们从一个实例对象读取一个属性时,首先是查找实例对象本身是否包含这个属性。如果没有,则遍历原型链,直到找到原型链中第一个包含该属性的对象,并返回该值。如果给实例对象的某个属性(可能是继承来的)赋值(基础数据类型,例如:number,string,boolean等),则该值只会保存在实例对象上,而非原型上。但是,如果你给原型的某个属性赋的是个 object values (Object,Array),那么所有实例对象访问的就是同一个共享值了。
为了避免不小心在实例间共享了数组或对象,对象属性一般赋为 null,然后在构造函数中进行初始化。(注:构造函数是在实例对象的空间中运行了,因此他赋的值只对该实例对象有效)。
declare(null,{ //非强制要求,但出于代码可读性考虑 //最好将所有属性声明放在这里 memberList:null,roomMap:null,constructor:function(){ //在构造器中初始化所有属性,确保数据就绪,便于其他方法使用 //并且不要赋null值或undefined this.memberList=[]; this.roomMap={}; } });
<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <Metacharset="UTF-8"> <title>declare</title> <scripttype="text/javascript"src="dojo/dojo.js"data-dojo-config="async:true"></script> </head> <body> <scripttype="text/javascript"> require(["dojo/_base/declare"],{ myMethod:function(){ console.log("Hello"); } }); //定义类B varB=declare(A,{ myMethod:function(name){ //调用类A的myMethod方法 //类A的myMethod方法提供的参数arguments this.inherited(arguments); console.log(name); } }); //创建类B的实例 varmyB=newB(); myB.myMethod("world"); }); </script> </body> </html>
输出结果:
"Hello"
"world"
注意:不要在 constructor 中调用this.inherited。
参考文献:1、http://dojotoolkit.org/documentation/tutorials/1.10/declare/
2、http://www.ibm.com/developerworks/cn/web/1203_xiejj_dojodeclare/
原文链接:https://www.f2er.com/dojo/291186.html