文章目錄
- 1.3.1 閉包
- 1.3.2 this關鍵字
- 1.3.3 apply函數的使用
- 1.3.4 基於apply的dojo.hitch
- 1.3.5 前後台互動XmlHttpRequest
1 前言1.1 傳統JS前台編程方式
傳統的前台編程模式如下
<HTML><br /><HEAD><br /><!—這裡引入頁面依賴的元素,包括JS 檔案<br /></HEAD><br /><BODY><br /><!—頁面HTML元素--><br /><SCRIPT><br /><!—使用者自己的指令碼代碼--><br /></SCRIPT><br /></BODY>
可以看出,傳統的前台編程方式是面向過程的,不便於擴充於重用。在一個複雜的BS項目中,如果能夠把抽象出介面中的某些具備共性的地區,把他們的行為(包括介面元素,介面事件,以及資料)封裝起來將大大前端的開發效率。
1.2 Dojo簡介
Dojo是一個工具箱,其定義了如下三大模組
1) 各式各樣的JS標準庫方法
這些標準庫方法包羅永珍並考慮了瀏覽器安全色性以及效能,在dojo架構下建議使用這些庫函數可以屏蔽瀏覽器的差異
2) 一應俱全的Web組件包括基本組件(dijit)以及擴充(dojox)
在web組件方面,可以把Dojo的Web組件看成一個前台標籤,頁面載入完畢後dojo將會調用dojo.parser模組的parse方法掃描但前頁面中所有具備dojotype屬性的DIV標籤,把它們parse成對應的web組件。這得一提的是這些組件全部是基於物件導向的思想設計,非常便於擴充,使用者在外部可以通過重新導向基類的方法,注入自己的介面行為,或者乾脆通過繼承來重載,覆蓋父類的方法定義自己的組件。
1.3 基本概念介紹
物件導向的思想已經深入人心。而Dojo的web組件也基於物件導向的思想,所以在進一步介紹web組件之前,需要介紹一下JS的物件導向特性。
1.3.1 閉包
閉包本質上就是資料與包含資料的範圍的結合體。請看下例
function foo ()<br />{<br />var x = 10;<br />return function bar()<br />{<br /> alert(x);<br />}<br />}<br />var x = 5;<br />var barRef = foo();<br />barRef();//結果為10
1.3.2 this關鍵字
This指的是當前代碼的調用者
例子1)
<mce:script type="text/javascript"><!--</p><p>alert(this == window);<br />// --></mce:script>
單獨運行在頁面中的JS代碼的this指的是window對象
例子2-1)
<select id =”sel” onchange=”test();”><OPTION>1</OPTION><OPTION>2</OPTION></select><br /><mce:script type="text/javascript"><!--</p><p>function test()<br />{<br /> alert(this == document.getElementById(‘sel’));<br />}<br />// --></mce:script>
例子2-2)
<select id =”sel” onchange=”test();”><OPTION>1</OPTION><OPTION>2</OPTION></select><br /><mce:script type="text/javascript"><!--</p><p>document.getElementById(‘sel’).onchange = test()<br />{<br /> alert(this == document.getElementById(‘sel’));<br />}<br />// --></mce:script>
由於onchange事件可以看成select的屬性,因此可以認為是select調用了onchange,所以事件響應代碼中的this 就是該DOM節點
例子3)
<mce:script type="text/javascript"><!--</p><p>var a = {};<br />a.b = function()<br />{<br /> alert(this == a)<br />}<br />a.b();<br />// --></mce:script>
這個例子比較明顯,由於是a調用了a.b所以在函數中。this就是a對象
1.3.3 apply函數的使用
在JavaScript中,一個函數其實是一個對象,任何一個函數都有一個apply方法,
apply方法JScript參考中的說明:應用某一對象的一個方法,用另一個對象替換當前對象。apply([thisObj[,argArray]])
apply([thisObj[,argArray]])中的參數為數組集合。下面來看看apply的具體應用
// simple apply demo<br />function simpleApplyDemo(arg) {<br />window.alert(arg);<br />}<br />function handleSPA(arg) {<br />simpleApplyDemo.apply(this, arguments);<br />}
從上面簡單的例子可以看出,apply可以把當前的參數傳遞給另外一個函數的參數中,從而調用另一個函數的應用。
apply還有一個技巧在裡面,就是用apply應用另一個函數(類)以後,當前的函數(類)就具備了另一個函數(類)的方法或者是屬性,這也可以稱之為“繼承”。看下面樣本。
// advanced apply demo<br />function adApplyDemo(x) {<br />return ("this is never-online, BlueDestiny '" + x + "' demo");<br />}<br />function handleAdApplyDemo(obj, fname, before) {<br /> var oldFunc = obj[fname];<br /> obj[fname] = function() {<br /> return oldFunc.apply(this, before(arguments));<br /> };<br />}<br />function hellowordFunc(args) {<br /> args[0] = "hello " + args[0];<br /> return args;<br />}<br />function applyBefore() {<br />alert(adApplyDemo("world"));<br />}<br />function applyAfter() {<br />handleAdApplyDemo(this, "adApplyDemo", hellowordFunc);<br />alert(adApplyDemo("world")); // Hello world!<br />}
1.3.4 基於apply的dojo.hitch
如果直接這麼寫obj.onclick=functiona(arguments1, arguments2){…..};
我們會發現在functiona中,this就是觸發click事件的Dom節點了
所以在我們的組件代碼中,經常出現如下代碼this.connect(obj, ‘click’, dojo.hitch(this, functiona, arguments1, arguments2));這樣functiona中的this和組件代碼中的this保持一致,也就可以訪問組件類中的屬性以及方法了
我們看一下dojo.hitch的代碼會發現如下片斷
return function(){ return scope[method].apply(scope, arguments || []); }; // Function,可見它正是利用了apply的特性實現了範圍的變更。
dojo.hitch參數表為,第一個參數為範圍,第二個參數為函數對象,從第三個參數起就是第二個參數所對應函數的參數表
1.3.5 前後台互動XmlHttpRequest
現代瀏覽器都支援XmlHttpRequest, XmlHttpRequest的調用都是非同步也就是說這樣的調用都無需頁面進行跳轉的。dojo再其上封裝了dojo.xhr類。dojo的web組件接受json(javascript object notion)資料。所以使用dojo組件無需考慮背景具體實現,只要後台能夠提供非同步請求機制,並且能夠返回Json即可。在BME R4版本中,基於structs2以及jsonlib開發了非同步請求action機制,調用這些action,BMER4版本定義了如下函數
ajax.callQueryInterface = function(url/*非同步action名稱*/,inData/*對象參數*/){<br /> var tmpData = dojo.toJson(inData); //將對象轉為Json格式字串<br /> var ret;<br /> var msg;<br /> dojo.xhrPost({//dojo封裝的xmlHttprequest方法<br /> url: url,<br /> content:{<br /> "data":tmpData<br /> },<br /> timeout: 1000,<br /> error: function() {//錯誤處理函數<br /> ret = "-1";<br /> msg=Message.getString("P405237800");<br /> },<br />load:function (response) {//處理返回<br />ret = dojo.fromJson(response);<br />。。。。。。。。。<br />。。。。。。。。。<br /> return ret;<br />};
2 組件開發介紹2.1 dojo.declare
dojo把與實作類別聲明和繼承有關的大量細節都封裝到一個方法中,這個方法就是dojo.declare,這個方法包括三個參數
dojo.declare(/*String*/className,//要建立的建構函式名稱
/*Function|Function[]*/superCalss,//要繼承的超類建構函式,如果是建構函式數組則是多重繼承
/*Object*/ //對象中的參數定義)
2.2 一個簡單的基於dojo開發的web組件----Hellow World
dojo.provide("bmc.widget.HelloWorld");<br />dojo.require("dijit.layout.ContentPane");<br />dojo.declare(<br /> " bmc.widget.HelloWorld",<br /> dijit.layout.ContentPane,<br /> {<br /> postCreate: function(){<br /> this.inherited(arguments);<br /> var html = 'This is a hello word widget!<BR><INPUT type = button value="alert"/>';<br /> var paneDiv = document.createElement('div');<br /> paneDiv.innerHTML = html;<br /> var button = paneDiv. getElementsByTagName('INPUT')[0]<br /> dojo.place(paneDiv, this.containerNode, 'first');<br /> this.dataForm = this.containerNode.getElementsByTagName('FORM')[0];<br /> dojo.connect(button, 'click', dojo.hitch(this, this.alertHellow));<br /> },<br /> alertHellow : function()<br /> {<br /> alert('hello');<br /> }<br />});
Html頁面
<div class='box' dojoType=" bmc.widget.HelloWorld"><br /></div>
這個例子中使用this.connect綁定dom事件一是這個方法不會破壞原來已有的事件方法,如果button原來已有click響應函數,則這個操作將會把dojo.hitch(this, this.alertHellow)這個函數的動作追加到原來的操作後面,同時在對象銷毀的時候(頁面跳轉,重新整理),凡是被connect的函數都會去綁定,可以防止IE的記憶體泄露。
這個例子中也使用了dojo.hitch,這個方法可以改變函數的範圍,如果不使用dojo.htich則alertHellow函數中的this指向這個按鈕,如果通過dojo.hitch作用,則這個函數種的this 指向這個組件的執行個體。通過這個機制,可以完全實現組件內部介面操作的封裝。
postCreate是父類dijit.layout.ContentPane的方法,通過執行this.inherited(arguments);,父類的代碼會被執行。如果沒有這個調用,父類的方法就被覆蓋了。
2.3 HelloWord升級,支援資料傳入的表單
dojo.provide("bmc.widget.HelloWorld");<br />dojo.require("dijit.layout.ContentPane");<br />dojo.declare(<br /> " bmc.widget.HelloWorld",<br /> dijit.layout.ContentPane,<br /> {<br /> data: null,<br /> postCreate: function(){<br /> this.inherited(arguments);<br /> var html = '<FORM><INPUT name = "a" /><INPUT name = "b" /><INPUT type="button" value="test" /></FORM>';<br /> var paneDiv = document.createElement('div');<br /> paneDiv.innerHTML = html;<br /> var button = paneDiv. getElementsByTagName('INPUT')[2]<br /> dojo.place(paneDiv, this.containerNode, 'first');<br /> this.dataForm = this.containerNode.getElementsByTagName('FORM')[0];<br /> dojo.connect(button, 'click', dojo.hitch(this, this.setData));<br /> }, </p><p> setData: function()<br /> {<br /> if (this.data)<br /> {<br /> var _this = this;<br /> dojo.forEach(this.dataForm.elements, function(item)<br /> {<br /> if (!item.name)<br /> {<br /> return;<br /> }<br /> item.value = _this.data[item.name];<br /> });<br /> }<br /> }<br />});
Html代碼
var datatest = {};<br />datatest.a = 1;<br />datatest.b = 2;<br /><div class='box' dojoType="dijit.layout.HelloWorld" data="datatest"><br /></div>
點擊test
這個方法中使用dojo.forEach遍曆表單元素。在function(item)
{
}
中this指向的不是組件執行個體,所以之前用一個變數_this儲存了this
2.4 總結
通過以上介紹,我們可以得出如下結論
1) Dojo是是一個純Javascript庫,後台只要提供相應的介面能夠將資料以Json的格式輸出給前台即可;
2) Dojo自身定義了完整的函數庫,屏蔽了瀏覽器的差異;
3) Dojo自身定義介面組件庫,其組件代碼採用了物件導向的思想,便於繼承以及擴充;只要組件庫豐富;
4) 當對前端介面聯動需求較為複雜的時候,基於dojo的頁面組件將是首選,因為其可以將介面中某一個具有共性的地區抽象出來,封裝這一地區的介面行為以及資料,可以用搭積木的方式完成複雜頁面的開發。