syuko

Just For Fun。生存,社会秩序,娱乐
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

JavaScript基础之继承(附实例)

Posted on 2008-03-18 08:29  syuko  阅读(5563)  评论(13编辑  收藏  举报


JavaScript基础之继承(附实例) 
 

    前些天写了一篇关于JavaScript基础的帖子"JavaScript基础之对象"。在那篇帖子中说马上就会写一些关于继承方面的东西。可是上周杂事太多,都没有时间动笔,就一直拖到今天才终于下笔写了。上一篇帖子中很多人认为我自己的东西太少了,所以这篇帖子中在说完基础知识之后我会给出一个完整的例子。

    大家都知道面向对象语言的三大特性为:封装、继承、多态。面向对象的语言之所以强大就全依赖于这三大特性。脚本作为一种准面向对象的语言虽然有先天的不足,但还是能在某种程度上去实现这些特性。实现最多、技术最成熟的就数继承了,不过脚本也不能像C#一样简单地用一个":"就能实现继承,而是要自己去实现继承的细节。

1 继承的几种实现方法

    要实现继承首先就必须要有子类、父类(超类、基类等叫法)。子类去继承父类的方法和属性。在脚本里所有开发人员定义的类都可以作为父类,不过本地类(native object)和宿主类(host object)不能作为父类。由于脚本里面没有访问修饰符,父类所有的属性和方法都可以被子类直接访问。子类也可以扩展父类的属性和方法。

1.1 call()方法

    Call方法是脚本的一个内置(build in)方法。官方说明为:调用一个对象的方法,以另一个对象替换当前对象。语法为:call([thisObj[,arg1[, arg2[, [,.argN]]]]])。用这个方法可以很容易地模拟继承的实现:

 1function funCreateElement(sClassName)
 2        {
 3            this.sAClassName=sClassName;
 4            
 5            this.createALink=function()
 6            {
 7                var oA=document.createElement("a");
 8                oA.className=this.sAClassName;
 9                return oA;
10            }

11        }

    类(funCreateElement)有一个属性(sAClassName)和一个方法(createALink)。它的作用是创建一个DOM元素。这个类使用this关键字给属性赋值,这样就确保了所有的实例指向的都是自己的值。

1function funCurrNavi()
2        {
3            // 继承元素创建类
4            funCreateElement.call(this,"tblNavigatorA");
5        }

    这样就实现了一个最简单的继承了。在funCurrNavi的实例里就可以直接使用funCreateElement类里面的方法了。

    var o=new funCurrNavi(); 
    
o.createALink();


1.2 apply()方法

    apply方法也是脚本的一个内置(build in)方法。其用法和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的这个特性,我们就可以实现继承方法了。如果用原型链的方式重定义前面的例子,它们将变为下面的形式:

 1function funCreateElement()
 2        {
 3        }

 4        
 5        funCreateElement.prototype.sAClassName=sClassName;  // 样式类
 6        funCreateElement.prototype.createALink=function()
 7        {
 8            var oA=document.createElement("a");
 9            oA.className=this.sAClassName;
10            return oA;
11        }
  
12        
13        
14        function funCurrNavi()
15        
16        }

17        
18        funCurrNavi.prototype=new funCreateElement();
19        funCurrNavi.prototype.showMeInDiv=function()
20        {
21        }


    这种方式采用funCurrNavi.prototype=new funCreateElement()方法,将funCreateElement类里面所有的属性和方法都复制到funCurrNavi类里面。非常的快捷。这种方式虽然非常的快捷,但是也有其缺点。采用这种方式时子类所有的属性和方法都必须在funCurrNavi.prototype=new funCreateElement()语句之后。因为该语句会覆盖funCurrNavi类原有的prototype类,添加了新的属性和方法的原有prototype类将被销毁。而且父类和子类都没有参数,只能通过设置属性的方法传入参数。


1.4 混合方式

    混合方式就是指将前两种方式结合起来使用,取长避短。在call方式里面定义类的方式不理想,相同的方法在实例里面被重复的实例化了。原型链的方法则因为没有参数而不理想,使用参数前得额外的去设置其属性,不如构造函数的参数来的方便。根据上一篇帖子中定义类的混合方式,我们重新定义funCreateElement类:

 1function funCreateElement(sClassName)
 2        {
 3            this.sAClassName=sClassName;
 4        }

 5        
 6        funCreateElement.prototype.createALink=function()
 7        {
 8            var oA=document.createElement("a");
 9            oA.className=this.sAClassName;
10            return oA;
11        }


然后在funCurrNavi类里面采用call方式继承funCreateElement类的属性,采用原型链的方式继承其方法。如下:

 1function funCurrNavi(sLinkText)
 2        {
 3            funCreateElement.call(this,"tblNavigatorA");
 4        
 5            this.LinkText=sLinkText;
 6        }

 7        
 8        funCurrNavi.prototype=new funCreateElement();   // 继承超链接类
 9        
10        funCurrNavi.prototype.showMeInDiv=function()
11        {
12            var oLink=this.createALink();
13            oLink.text=this.LinkText;
14        }

    采用funCreateElement.call(this,"tblNavigatorA")方式继承其参数,funCurrNavi.prototype=new funCreateElement()方式继承其方法。从而达到方法不会被实例化多次的目的。

2 实例

     前面讲述了几种继承的实现方式,这一小节就利用前面讲述的方法去练习一个小例子。这个例子是根据一个字符串去呈现一个树状的列表,这种需求常见于页面的导航中。由于只是为了辅助讲述继承,例子有些地方不免有些牵强附会和小题大做的地方。这些就希望各位网友包涵了,请各位珍惜自己的砖头了。我在这里戴上钢盔等候大家的批评了。:)

    下面是其运行成功的样图:
 
    左面的那个是用table作为容器,右边的那个是用div作为容器。

  1<html xmlns="http://www.w3.org/1999/xhtml">
  2<head>
  3    <title>Create Admin Pages' Navigator</title>
  4    <style type="text/css">
  5        /*导航table的样式*/.tblNavigator
  6        {
  7            border: 2px solid #FFD7AC;
  8            width: 200px;
  9            margin: 3px;
 10            background: #fff;
 11            margin-top: 5px;
 12            margin-bottom: 5px;
 13            border-collapse: collapse;
 14        }

 15        /*每个根节点的样式*/.tblNavigatorRoot
 16        {
 17            height: 30px;
 18            line-height: 30px;
 19            text-align: left;
 20            vertical-align: middle;
 21        }

 22        /*根节点的子节点的样式*/.tblNavigatorTD
 23        {
 24            border-collapse: collapse;
 25            border: 1px solid #FFE6CB;
 26            height: 22px;
 27            text-indent: 15px;
 28            background: #fff;
 29            width: 100%;
 30        }

 31        /*导航中的超链接*/.tblNavigatorA
 32        {
 33            font-weight: 700;
 34            text-decoration: none;
 35            color: Black;
 36            width: 100px;
 37        }

 38    </style>
 39</head>
 40<body>
 41    <div id="divMain">
 42    </div>
 43    <div id="divMain2" class="tblNavigator">
 44    </div>
 45
 46    <script type="text/javascript">
 47        
 48        var sNavigatorMain = 
 49        
 50            sNavigators:[
 51            {
 52                root:[{name:'root1'}],
 53                sons:[{name:'root1Son1',url:'bcc'},{name:'root1Son2',url:'bcc'}]
 54            }
,
 55            {
 56                root:[{name:'root2'}],
 57                sons:[{name:'root2Son1',url:'bcc'}]
 58            }
,
 59            {
 60                root:[{name:'root3'}],
 61                sons:[{name:'root3Son1',url:'bcc'},{name:'root3Son2',url:'bcc'},{name:'root3Son3',url:'bcc'}]
 62            }
,
 63            {
 64                root:[{name:'root4'}],
 65                sons:[{name:'root4Son1',url:'bcc'},{name:'root4Son3',url:'bcc'}]
 66            }

 67            ,
 68            {
 69                root:[{name:'root4'}],
 70                sons:[{name:'root4Son1',url:'bcc'},{name:'root4Son3',url:'bcc'}]
 71            }

 72            ]
 73        }
;  
 74        
 75        /*
 76         * 创建一个元素
 77         * sClassName:元素的样式控制类
 78        */

 79        function funCreateElement(sClassName)
 80        {
 81            // 样式类
 82            this.sAClassName=sClassName;
 83        }

 84        
 85        /*
 86         * 创建超链接元素
 87         * sText:超链接显示的文本
 88         * sUrl:超链接的href
 89         * sTarget:打开链接的目标
 90         * 返回:超链接元素
 91         */

 92        funCreateElement.prototype.createALink=function(sText,sUrl,sTarget)
 93        {
 94            var oA=document.createElement("a");
 95            oA.href=sUrl;                       // 链接文本
 96            oA.className=this.sAClassName;      // 样式类
 97            oA.target=sTarget;            // 打开链接的目标
 98            oA.appendChild(document.createTextNode(sText));     // 显示文本
 99            
100            return oA;
101        }

102        
103        /*
104         * 创建Div元素
105         * sText:显示的文本
106         * 返回:Div元素
107         */

108        funCreateElement.prototype.createDivBlank=function()
109        {
110            var oDiv=document.createElement("div");
111            oDiv.className=this.sAClassName;
112            return oDiv;
113        }

114        
115        /*
116         * 创建Div元素
117         * sText:显示的文本
118         * 返回:Div元素
119         */

120        funCreateElement.prototype.createDivNoLink=function(sText)
121        {
122            var oDiv=document.createElement("div");
123            oDiv.className=this.sAClassName;
124            oDiv.appendChild(document.createTextNode(sText));
125            
126            return oDiv;
127        }

128        
129        /* 
130         * 创建一个模块的节点
131         * oNaviJSON:一个模块节点的文本
132         * oParentObject:父节点
133         * sClassNameRoot:根节点的样式类
134         * sClassNameNode:子节点的样式类
135         */

136        function funCurrNavi(oNaviJSON,oParentObject,sClassNameRoot,sClassNameNode)
137        {
138            // 继承元素创建类
139            funCreateElement.call(this,"tblNavigatorA");
140        
141            // 当前导航的菜单
142            this.oNavi=oNaviJSON;
143            // 当前导航的父节点
144            this.oParent=oParentObject;
145            // 根节点的样式类
146            this.oNClassNameRoot=sClassNameRoot;
147            // 子节点的样式类
148            this.oNClassNameNode=sClassNameNode;
149        }

150        
151        // 继承超链接类
152        funCurrNavi.prototype=new funCreateElement();
153        
154        // 显示自己(在Table容器里)
155        funCurrNavi.prototype.showMeInTable=function()
156        {
157            // 在父容器中添加一行,并将父节点写入
158            this.oParent.insertRow(0);
159            this.oParent.rows[0].onclick=function(){alert(this.innerText)};
160            this.oParent.rows[0].insertCell(0);
161            this.oParent.rows[0].cells[0].className=this.oNClassName;
162            this.oParent.rows[0].cells[0].appendChild(document.createTextNode(this.oNavi.root[0].name));
163                        
164            var j=this.oNavi.sons.length;
165            for(var i=0;i<j;i++)
166            {
167                this.oParent.insertRow(i+1);
168                this.oParent.rows[i+1].insertCell(0);
169                this.oParent.rows[i+1].cells[0].className=this.oNClassNameNode;
170                this.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
175        funCurrNavi.prototype.showMeInDiv=function()
176        {
177            // 创建一个暂时的容器
178            var oDivContainer=document.createElement("div");
179            
180            // 在父容器中将父节点写入
181            var oDivRoot=this.createDivNoLink(this.oNavi.root[0].name);
182            oDivRoot.className=this.oNClassNameRoot;
183            oDivRoot.onclick=function(){alert(this.innerText);};
184            oDivContainer.appendChild(oDivRoot);
185            
186            // 链接节点
187            var oDivLinkNode;
188                                    
189            var j=this.oNavi.sons.length;
190            for(var i=0;i<j;i++)
191            {
192                oDivLinkNode=this.createDivBlank();
193                oDivLinkNode.appendChild(this.createALink(this.oNavi.sons[i].name,this.oNavi.sons[i].url,"_blank"));
194                oDivLinkNode.className=this.oNClassNameNode;
195                oDivContainer.appendChild(oDivLinkNode);
196            }

197            
198            return oDivContainer;
199        }

200        
201        
202        /* 
203         * 创建所有的节点
204         * oNavisJSON:所有节点的文本
205         */

206        function funCurrNavis(oNavisJSON,sClassName)
207        {
208            // 当前导航的菜单
209            this.oNavis=oNavisJSON;
210            // 样式类
211            this.oNsClassName=sClassName;
212        }

213        
214        // 创建所有的节点(以Table为容器)
215        funCurrNavis.prototype.createNavigatorsInTable=function()
216        {
217            // 创建父Table
218            var oTable=document.createElement("table");
219            oTable.className=this.oNsClassName;
220            
221            // 创建tbody
222            var oTBody=document.createElement("tbody");
223            oTable.appendChild(oTBody);
224            
225            var j=this.oNavis.sNavigators.length;
226            var CNavi;
227            
228            for(var i=j-1;i>=0;i--)
229            {
230                 CNavi=new funCurrNavi(this.oNavis.sNavigators[i],oTBody,"tblNavigatorRoot","tblNavigatorTD");
231                 CNavi.showMeInTable();
232            }

233            
234            return oTable;
235        }

236        
237        // 创建所有的节点(以Div为容器)
238        funCurrNavis.prototype.createNavigatorsInDiv=function()
239        {
240            // 创建容器
241            var oContainer=document.createDocumentFragment();
242            
243            var j=this.oNavis.sNavigators.length;
244            var CNavi;
245            
246            for(var i=0;i<j;i++)
247            {
248                 CNavi=new funCurrNavi(this.oNavis.sNavigators[i],{},"tblNavigatorRoot","tblNavigatorTD");
249                 oContainer.appendChild(CNavi.showMeInDiv());
250            }

251            
252            return oContainer;
253        }

254        
255        /*
256         * 供调用的函数
257         * sNavigatorMain:所有节点的文本
258         * sParentID:导航父容器的ID
259        */

260        function showNavigator(sNavigatorMain,sParentID,sFlag)
261        {
262            var oTemp=eval("ddkk="+sNavigatorMain);
263            var main=new funCurrNavis(oTemp,"tblNavigator");
264            
265            if(sFlag=="div")
266            {
267                document.getElementById(sParentID).appendChild(main.createNavigatorsInDiv());
268            }

269            else
270            {
271                document.getElementById(sParentID).appendChild(main.createNavigatorsInTable());
272            }

273        }

274        
275    </script>
276
277    <script type="text/javascript">
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/frmUserMng.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    </script>
282
283</body>
284</html>

    在这个实例里面采用了JSON字符串作为数据源"{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'}]}]}"。其格式好了的形式类似于上面例子中的

 1var sNavigatorMain = 
 2        
 3            sNavigators:[
 4            {
 5                root:[{name:'root1'}],
 6                sons:[{name:'root1Son1',url:'bcc'},{name:'root1Son2',url:'bcc'}]
 7            }
,
 8            {
 9                root:[{name:'root2'}],
10                sons:[{name:'root2Son1',url:'bcc'}]
11            }
,
12            {
13                root:[{name:'root3'}],
14                sons:[{name:'root3Son1',url:'bcc'},{name:'root3Son2',url:'bcc'},{name:'root3Son3',url:'bcc'}]
15            }
,
16            {
17                root:[{name:'root4'}],
18                sons:[{name:'root4Son1',url:'bcc'},{name:'root4Son3',url:'bcc'}]
19            }

20            ,
21            {
22                root:[{name:'root4'}],
23                sons:[{name:'root4Son1',url:'bcc'},{name:'root4Son3',url:'bcc'}]
24            }

25            ]
26        }
;  

用JSON字符串作为数据源非常的方便,他能直接转化成类。上面的例子非常简单就不详细地说明了,通过注释就能看明白了。

    这篇帖子我希望各位兄台就技术方面多提提意见。千万别扔砖头!!

    相关文章:"JavaScript基础之对象";