全域變數是魔鬼。在YUI中,我們僅用兩個全域變數:YAHOO和YAHOO_config。YUI的一切都是使用YAHOO對象級的成員或這個成員範圍內的變數。我們建議在你的應用程式也使用類似的規則。
Douglas Crockford已經傳授了一個有用的單例模式(singleton pattern)實現此規則,我認為他的模式有益於你基於YUI的那些應用。Douglas叫它模組模式(module pattern)。它是如下工作的:
- 建立一個命名空間對象:如果你使用YUI,可以用YAHOO.namespace()方法:
YAHOO.namespace("myProject");
這分配了一個空的myProject對象,是YAHOO的一個成員(如 果myProject已存在的話,則不會被覆蓋)。現在我們可以開始添加YAHOO.myProject的成員。
- 對你的命名空間對象分配一個匿名函數傳回值:
YAHOO.myProject.myModule = function () {return { myPublicProperty: "我作為YAHOO.myProject.myModule.myPublicProperty被訪問。"; myPublicMethod: function () { YAHOO.log("我作為YAHOO.myProject.myModule.myPublicMethod被訪問。"); }};}(); // 這個括弧導致匿名函數被執行且返回
注意有閉合大括弧和緊接著的括弧()的最後一行—這種符號導致了匿名函數的立即執行,返回包含myPublicProperty和myPublicMethod的對象。只要這個匿名函數一返回,返回對象就作為YAHOO.myProject.myModule被訪問。
- 在匿名函數中,在返回語句前加入“私人”方法和變數。到目前為止,我們只是將myPublicProperty和myPublicMethod直接分配到YAHOO.myProject.myModule中。此外,當我們在返回語句之前放置一些代碼時,這個模式還支援被增加的效用。
YAHOO.myProject.myModule = function () {//“私人”變數:var myPrivateVar = "我僅能在YAHOO.myProject.myModule內被訪問。";//私人方法:var myPrivateMethod = function () { YAHOO.log("我僅能在YAHOO.myProject.myModule內被訪問。");}return {myPublicProperty: "我作為YAHOO.myProject.myModule.myPublicProperty能被訪問。"myPublicMethod: function () {YAHOO.log("我作為YAHOO.myProject.myModule.myPublicMethod能被訪問。");//在myProject,我能訪問私人的變數和方法YAHOO.log(myPrivateVar);YAHOO.log(myPrivateMethod());//myPublicMethod的原生範圍是myProject,我們可以用“this”來訪問公用成員。YAHOO.log(this.myPublicProperty);}};}();
在上面的代碼中,我們從一個匿名函數返回有兩個成員的一個對象。在YAHOO.myProject.myModule內部,可以分別用this.myPublicProperty和this.myPublicMethod來訪問。在YAHOO.myProject.myModule外部,公用成員可以用YAHOO.myProject.myModule.myPublicProperty和YAHOO.myProject.myModule.myPublicMethod來訪問。
私人變數myPrivateProperty和myPrivateMethod只能被匿名函數本身或返回對象的成員訪問。儘管匿名函數會立即執行和終止,但它們依然是保留著,憑藉閉包(closure)的力量——通過一個函數的局部變數在這個函數返回後是保留的規則。只要 YAHOO.myProject.myModule需要它們,我們的兩個私人變數就不會被銷毀。
- 實踐這個模式。讓我們來看看這個模式的一個常見應用案例。假設你有一個列表,列表上的一些項可以被拖拽。應用拖拽的項上有拖拽的CSS類。
<!--這個指令檔包含所有的YUI公用程式--> <script type="text/javascript"src="http://yui.yahooapis.com/2.2.2/build/utilities/utilities.js"></script><ul id="myList"> <li class="draggable">一項</li> <li>二項</li> <!--二項將不能被拖拽--> <li class="draggable">三項</li> </ul><script> YAHOO.namespace("myProject"); YAHOO.myProject.myModule = function () { //YUI公用程式的私人簡寫引用: var yue = YAHOO.util.Event, yud = YAHOO.util.Dom; //私人方法 var getListItems = function () { // 注意這個地方使用其他的私人變數,包括"yud"YAHOO.util.Dom的簡寫: var elList = yud.get("myList"); var aListItems = yud.getElementsByClassName( "draggable", //得到僅有CSS類"draggable"的項 "li", //僅返回清單項目 elList //限定搜尋改元素的子 ); return aListItems; }; //這個放回的對象將變成YAHOO.myProject.myModule: return { aDragObjects: [], //可對外訪問的,儲存DD對象 init: function () { //直到DOM完全載入好,才實現清單項目可拖拽: yue.onDOMReady(this.makeLIsDraggable, this, true); }, makeLIsDraggable: function () { var aListItems = getListItems(); //我們可以拖拽的那些元素 for (var i=0, j=aListItems.length; i<j; i++) { this.aDragObjects.push(new YAHOO.util.DD(aListItems[i])); } } }; }();//上面的代碼已經執行,所以我們能立即訪問init方法: YAHOO.myProject.myModule.init(); </script>
這是一個簡單的例子,特意寫的詳細一些——如果按照這種方式做,我們無疑能把它寫的更緊湊。當項目變得更加複雜和它的API增加,這個模式縮放的很好。通過這種方式,它避免了全域命名空間,提供了對外的可以訪問的API方法,支援受保護或“私人”的資料和方法。
- [1]原文:《a javascript module pattern》。這是在YUI blog上的,有的地方可能打不開,可以搜一下英文的轉載或者利用搜尋引擎的緩衝也能看。
- [2]《A JavaScript Module Pattern – JavaScript的一種模組模式》這是別人的翻譯,參考了不少,不過感覺挺不方便看的,這是我翻譯的這篇文章的一個原因,當然最主要的原因是這篇文章算是學習YUI的最基礎的文章了,整個YUI的模組模式都基於此。
原文地址:http://dancewithnet.com/2007/12/04/a-javascript-module-pattern/