|
JavaScript基础之继承
前些天写了一篇关于JavaScript基础的帖子"JavaScript基础之对象"。在那篇帖子中说马上就会写一些关于继承方面的东西。可是上周杂事太多,都没有时间动笔,就一直拖到今天才终于下笔写了。上一篇帖子中很多人认为我自己的东西太少了,所以这篇帖子中在说完基础知识之后我会给出一个完整的例子。
大家都知道面向对象语言的三大特性为:封装、继承、多态。面向对象的语言之所以强大就全依赖于这三大特性。脚本作为一种准面向对象的语言虽然有先天的不足,但还是能在某种程度上去实现这些特性。实现最多、技术最成熟的就数继承了,不过脚本也不能像C#一样简单地用一个":"就能实现继承,而是要自己去实现继承的细节。
1继承的几种实现方法
要实现继承首先就必须要有子类、父类(超类、基类等叫法)。子类去继承父类的方法和属性。在脚本里所有开发人员定义的类都可以作为父类,不过本地类(nativeobject)和宿主类(hostobject)不能作为父类。由于脚本里面没有访问修饰符,父类所有的属性和方法都可以被子类直接访问。子类也可以扩展父类的属性和方法。
1.1call()方法
Call方法是脚本的一个内置(buildin)方法。官方说明为:调用一个对象的方法,以另一个对象替换当前对象。语法为:call([thisObj[,arg1[,arg2[,[,.argN]]]]])。用这个方法可以很容易地模拟继承的实现:
类(funCreateElement)有一个属性(sAClassName)和一个方法(createALink)。它的作用是创建一个DOM元素。这个类使用this关键字给属性赋值,这样就确保了所有的实例指向的都是自己的值。
1functionfunCreateElement(sClassName)
2{
3this.sAClassName=sClassName;
4
5this.createALink=function()
6{
7varoA=document.createElement("a");
8oA.className=this.sAClassName;
9returnoA;
10}
11}
1functionfunCurrNavi()
2{
3//继承元素创建类
4funCreateElement.call(this,"tblNavigatorA");
5}
这样就实现了一个最简单的继承了。在funCurrNavi的实例里就可以直接使用funCreateElement类里面的方法了。
varo=newfunCurrNavi();
o.createALink();
1.2apply()方法
apply方法也是脚本的一个内置(buildin)方法。其用法和call对象类似。官方说明为:应用某一对象的一个方法,用另一个对象替换当前对象。语法为:apply([thisObj[,argArray]])。实际上这两个的作用几乎是相同的,要注意的地方是call(thisObj[,arg1[,arg2[,)中的arg参数可以是变量,而apply([thisObj[,argArray]])中的参数为数组集合。由于和call方法就只有参数的不同而已所以就不举例子了。
1.3原型链
Prototype对象是所有类的源头。如果修改了prototype类的方法则这个修改会反映到所有类的实例里面。比如:在string.prototype里面加一个方法trim(),String.prototype.trim=function(){}。那么所有的字符串都会具有trim()方法了("aa".trim())。由于prototype的这个特性,我们就可以实现继承方法了。如果用原型链的方式重定义前面的例子,它们将变为下面的形式:
这种方式采用funCurrNavi.prototype=newfunCreateElement()方法,将funCreateElement类里面所有的属性和方法都复制到funCurrNavi类里面。非常的快捷。这种方式虽然非常的快捷,但是也有其缺点。采用这种方式时子类所有的属性和方法都必须在funCurrNavi.prototype=newfunCreateElement()语句之后。因为该语句会覆盖funCurrNavi类原有的prototype类,添加了新的属性和方法的原有prototype类将被销毁。而且父类和子类都没有参数,只能通过设置属性的方法传入参数。
1functionfunCreateElement()
2{
3}
4
5funCreateElement.prototype.sAClassName=sClassName;//样式类
6funCreateElement.prototype.createALink=function()
7{
8varoA=document.createElement("a");
9oA.className=this.sAClassName;
10returnoA;
11}
12
13
14functionfunCurrNavi()
15{
16}
17
18funCurrNavi.prototype=newfunCreateElement();
19funCurrNavi.prototype.showMeInDiv=function()
20{
21}
1.4混合方式
混合方式就是指将前两种方式结合起来使用,取长避短。在call方式里面定义类的方式不理想,相同的方法在实例里面被重复的实例化了。原型链的方法则因为没有参数而不理想,使用参数前得额外的去设置其属性,不如构造函数的参数来的方便。根据上一篇帖子中定义类的混合方式,我们重新定义funCreateElement类:
1functionfunCreateElement(sClassName)
2{
3this.sAClassName=sClassName;
4}
5
6funCreateElement.prototype.createALink=function()
7{
8varoA=document.createElement("a");
9oA.className=this.sAClassName;
10returnoA;
11}
然后在funCurrNavi类里面采用call方式继承funCreateElement类的属性,采用原型链的方式继承其方法。如下:
1functionfunCurrNavi(sLinkText)
2{
3funCreateElement.call(this,"tblNavigatorA");
4
5this.LinkText=sLinkText;
6}
7
8funCurrNavi.prototype=newfunCreateElement();//继承超链接类
9
10funCurrNavi.prototype.showMeInDiv=function()
11{
12varoLink=this.createALink();
13oLink.text=this.LinkText;
14}
采用funCreateElement.call(this,"tblNavigatorA")方式继承其参数,funCurrNavi.prototype=newfunCreateElement()方式继承其方法。从而达到方法不会被实例化多次的目的。
2实例
前面讲述了几种继承的实现方式,这一小节就利用前面讲述的方法去练习一个小例子。这个例子是根据一个字符串去呈现一个树状的列表,这种需求常见于页面的导航中。由于只是为了辅助讲述继承,例子有些地方不免有些牵强附会和小题大做的地方。这些就希望各位网友包涵了,请各位珍惜自己的砖头了。我在这里戴上钢盔等候大家的批评了。:)
下面是其运行成功的样图:
左面的那个是用table作为容器,右边的那个是用div作为容器。
1
2
3CreateAdminPages''Navigator
4
5/导航table的样式/.tblNavigator
6{
7border:2pxsolid#FFD7AC;
8width:200px;
9margin:3px;
10background:#fff;
11margin-top:5px;
12margin-bottom:5px;
13border-collapse:collapse;
14}
15/每个根节点的样式/.tblNavigatorRoot
16{
17height:30px;
18line-height:30px;
19text-align:left;
20vertical-align:middle;
21}
22/根节点的子节点的样式/.tblNavigatorTD
23{
24border-collapse:collapse;
25border:1pxsolid#FFE6CB;
26height:22px;
27text-indent:15px;
28background:#fff;
29width:100%;
30}
31/导航中的超链接/.tblNavigatorA
32{
33font-weight:700;
34text-decoration:none;
35color:Black;
36width:100px;
37}
38
39
40
41
42
43
44
45
46
47
48varsNavigatorMain=
49{
50sNavigators:[
51{
52root:[{name:''root1''}],
53sons:[{name:''root1Son1'',url:''bcc''},{name:''root1Son2'',url:''bcc''}]
54},
55{
56root:[{name:''root2''}],
57sons:[{name:''root2Son1'',url:''bcc''}]
58},
59{
60root:[{name:''root3''}],
61sons:[{name:''root3Son1'',url:''bcc''},{name:''root3Son2'',url:''bcc''},{name:''root3Son3'',url:''bcc''}]
62},
63{
64root:[{name:''root4''}],
65sons:[{name:''root4Son1'',url:''bcc''},{name:''root4Son3'',url:''bcc''}]
66}
67,
68{
69root:[{name:''root4''}],
70sons:[{name:''root4Son1'',url:''bcc''},{name:''root4Son3'',url:''bcc''}]
71}
72]
73};
74
75/
76创建一个元素
77sClassName:元素的样式控制类
78/
79functionfunCreateElement(sClassName)
80{
81//样式类
82this.sAClassName=sClassName;
83}
84
85/
86创建超链接元素
87sText:超链接显示的文本
88sUrl:超链接的href
89sTarget:打开链接的目标
90返回:超链接元素
91/
92funCreateElement.prototype.createALink=function(sText,sUrl,sTarget)
93{
94varoA=document.createElement("a");
95oA.href=sUrl;//链接文本
96oA.className=this.sAClassName;//样式类
97oA.target=sTarget;//打开链接的目标
98oA.appendChild(document.createTextNode(sText));//显示文本
99
100returnoA;
101}
102
103/
104创建Div元素
105sText:显示的文本
106返回:Div元素
107/
108funCreateElement.prototype.createDivBlank=function()
109{
110varoDiv=document.createElement("div");
111oDiv.className=this.sAClassName;
112returnoDiv;
113}
114
115/
116创建Div元素
117sText:显示的文本
118返回:Div元素
119/
120funCreateElement.prototype.createDivNoLink=function(sText)
121{
122varoDiv=document.createElement("div");
123oDiv.className=this.sAClassName;
124oDiv.appendChild(document.createTextNode(sText));
125
126returnoDiv;
127}
128
129/
130创建一个模块的节点
131oNaviJSON:一个模块节点的文本
132oParentObject:父节点
133sClassNameRoot:根节点的样式类
134sClassNameNode:子节点的样式类
135/
136functionfunCurrNavi(oNaviJSON,oParentObject,sClassNameRoot,sClassNameNode)
137{
138//继承元素创建类
139funCreateElement.call(this,"tblNavigatorA");
140
141//当前导航的菜单
142this.oNavi=oNaviJSON;
143//当前导航的父节点
144this.oParent=oParentObject;
145//根节点的样式类
146this.oNClassNameRoot=sClassNameRoot;
147//子节点的样式类
148this.oNClassNameNode=sClassNameNode;
149}
150
151//继承超链接类
152funCurrNavi.prototype=newfunCreateElement();
153
154//显示自己(在Table容器里)
155funCurrNavi.prototype.showMeInTable=function()
156{
157//在父容器中添加一行,并将父节点写入
158this.oParent.insertRow(0);
159this.oParent.rows[0].onclick=function(){alert(this.innerText)};
160this.oParent.rows[0].insertCell(0);
161this.oParent.rows[0].cells[0].className=this.oNClassName;
162this.oParent.rows[0].cells[0].appendChild(document.createTextNode(this.oNavi.root[0].name));
163
164varj=this.oNavi.sons.length;
165for(vari=0;i 166{
167this.oParent.insertRow(i+1);
168this.oParent.rows[i+1].insertCell(0);
169this.oParent.rows[i+1].cells[0].className=this.oNClassNameNode;
170this.oParent.rows[i+1].cells[0].appendChild(this.createALink(this.oNavi.sons[i].name,this.oNavi.sons[i].url,"_blank"));
171}
172}
173
174//显示自己(在Div容器里)返回:div
175funCurrNavi.prototype.showMeInDiv=function()
176{
177//创建一个暂时的容器
178varoDivContainer=document.createElement("div");
179
180//在父容器中将父节点写入
181varoDivRoot=this.createDivNoLink(this.oNavi.root[0].name);
182oDivRoot.className=this.oNClassNameRoot;
183oDivRoot.onclick=function(){alert(this.innerText);};
184oDivContainer.appendChild(oDivRoot);
185
186//链接节点
187varoDivLinkNode;
188
189varj=this.oNavi.sons.length;
190for(vari=0;i 191{
192oDivLinkNode=this.createDivBlank();
193oDivLinkNode.appendChild(this.createALink(this.oNavi.sons[i].name,this.oNavi.sons[i].url,"_blank"));
194oDivLinkNode.className=this.oNClassNameNode;
195oDivContainer.appendChild(oDivLinkNode);
196}
197
198returnoDivContainer;
199}
200
201
202/
203创建所有的节点
204oNavisJSON:所有节点的文本
205/
206functionfunCurrNavis(oNavisJSON,sClassName)
207{
208//当前导航的菜单
209this.oNavis=oNavisJSON;
210//样式类
211this.oNsClassName=sClassName;
212}
213
214//创建所有的节点(以Table为容器)
215funCurrNavis.prototype.createNavigatorsInTable=function()
216{
217//创建父Table
218varoTable=document.createElement("table");
219oTable.className=this.oNsClassName;
220
221//创建tbody
222varoTBody=document.createElement("tbody");
223oTable.appendChild(oTBody);
224
225varj=this.oNavis.sNavigators.length;
226varCNavi;
227
228for(vari=j-1;i>=0;i--)
229{
230CNavi=newfunCurrNavi(this.oNavis.sNavigators[i],oTBody,"tblNavigatorRoot","tblNavigatorTD");
231CNavi.showMeInTable();
232}
233
234returnoTable;
235}
236
237//创建所有的节点(以Div为容器)
238funCurrNavis.prototype.createNavigatorsInDiv=function()
239{
240//创建容器
241varoContainer=document.createDocumentFragment();
242
243varj=this.oNavis.sNavigators.length;
244varCNavi;
245
246for(vari=0;i 247{
248CNavi=newfunCurrNavi(this.oNavis.sNavigators[i],{},"tblNavigatorRoot","tblNavigatorTD");
249oContainer.appendChild(CNavi.showMeInDiv());
250}
251
252returnoContainer;
253}
254
255/
256供调用的函数
257sNavigatorMain:所有节点的文本
258sParentID:导航父容器的ID
259/
260functionshowNavigator(sNavigatorMain,sParentID,sFlag)
261{
262varoTemp=eval("ddkk="+sNavigatorMain);
263varmain=newfunCurrNavis(oTemp,"tblNavigator");
264
265if(sFlag=="div")
266{
267document.getElementById(sParentID).appendChild(main.createNavigatorsInDiv());
268}
269else
270{
271document.getElementById(sParentID).appendChild(main.createNavigatorsInTable());
272}
273}
274
275
276
277
278//showNavigator("{sNavigators:[{root:[{name:''用户管理''}],sons:[{name:''个人信息管理'',url:''~/UserMng/frmMyInfoMng.aspx''},{name:''本单位用户管理'',url:''~/UserMng/frmUserMng.aspx''},{name:''本单位信息管理'',url:''~/SysMaintenance/frmDeptInfo.aspx''}]},{root:[{name:''申报管理''}],sons:[{name:''项目管理'',url:''~/Declaration/frmProjAppMng.aspx''}]},{root:[{name:''留言板管理''}],sons:[{name:''给管理员留言'',url:''~/Information/frmQuestion.aspx''}]}]}","divMain","table");
279
280//showNavigator("{sNavigators:[{root:[{name:''用户管理''}],sons:[{name:''个人信息管理'',url:''~/UserMng/frmMyInfoMng.aspx''},{name:''本单位用户管理'',url:''~/UserMng/frmUserMngwww.wang027.com.aspx''},{name:''本单位信息管理'',url:''~/SysMaintenance/frmDeptInfo.aspx''}]},{root:[{name:''申报管理''}],sons:[{name:''项目管理'',url:''~/Declaration/frmProjAppMng.aspx''}]},{root:[{name:''留言板管理''}],sons:[{name:''给管理员留言'',url:''~/Information/frmQuestion.aspx''}]}]}","divMain2","div");
281
282
283
284 | | |