DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> 關於JavaScript >> JS應用在Firebug中的擴展架構模式
JS應用在Firebug中的擴展架構模式
編輯:關於JavaScript     

網頁制作Webjx文章簡介:全局變量是魔鬼,這句話在JavaScript存在的地方應該就是成立的,當然Firefox擴展也不例外.

全局變量是魔鬼,這句話在JavaScript存在的地方應該就是成立的,當然Firefox擴展也不例外,如果大家把多於一個的對象置於全局命名空間下,和其他擴展的沖突是很容易發生的,而且發現這種沖突引起的錯誤是很困難的,因為每個人的擴展列表都不一樣啊。避免全局名字污染已經成了一個基本原則,本文從這點引申,介紹了一個應用在Firebug中的擴展架構模式,非常值得推薦。

【原文】Firefox Extensions: Global Namespace Pollution
【作者】Jan Odvarko
【譯文】http://cuimingda.com/2009/01/
【譯者】明達

以下是對原文的翻譯

最近有幾個開發者向我咨詢如何設計Firefox擴展的架構,第一個顯現在我腦海中的答案就是要合理定義那些在ChromeWindow作用域下的全局變量。

不合理的定義全局變量,可以輕易的引發不同擴展之間的沖突,而這些完全是應該避免的(這也是AMO審閱的步驟之一),因為沖突所引發的問題是很難被發現的。就目前的開發環境來說,全局變量就是魔鬼,尤其是采用OOP開發模式的時候。

我不想重復介紹如何從頭開始開發一個Firefox擴展,對於這方面已經有很多非常詳細的文章。本文的重點放在如何設計一個更加易於維護的Firefox擴展架構。

如果你對前面的介紹感興趣,那就接著看吧。。。

命名空間架構

擴展之間發生沖突的重要原因就是因為定義了不合理的全局變量。我認為對每個擴展來說,只有一個全局變量已經很足夠了(可以根據擴展的信息來定義這個唯一的全局變量的名字,比如可以是擴展的名字、域名、地址等),不僅可以滿足我們的開發,而且可以避免那些令人討厭的沖突。

Firebug使用的命名空間架構,基本建立在著名的Module Pattern基礎上(這種模式最早由Douglas Crockfod定義)下的。這種模式簡單而清晰,但其實我在很長時間裡都不是很明確這種模式究竟是如何工作的(I hadn’t understand how it actually works for a long time)。我相信每個開發者都可以充分利用這個方法。

基本的思路是將每個JavaScript腳本文件放進自己的作用域,這是通過一個函數來實現的,沒有定義任何全局變量,比如下面這段代碼:

function() {
  // TODO: 腳本文件中的全部代碼
}

我管這個函數就叫做命名空間。擺在眼前的第一個問題是,如何確定這個函數的內容會在正確的時間被調用。第二個問題是,如何在多個腳本文件中共享對象(這個會在後面的章節解答)。 Firebug通過將所有的命名空間進行注冊,並在Firefox chrome UI加載的時候調用來解決第一個問題,也就是下面這段代碼:

myExtension.ns(function()
{
  // TODO: 腳本文件中的全部代碼
});

命名空間(就是原來定義的那個函數)為當作myExtension.ns函數的一個參數,而myExtension對象是這個擴展中定義的唯一全局變量。這個對象代表著整個擴展。不用擔心這個名字太長,我們可以為他建立個快捷方式(在實際開發中,這個名字可能會類似 comSoftwareIsHardMyExtension這個樣子)。

ns函數比較簡單,就是把所有的方法都添加到一個數組中。

var namespaces = [];
this.ns = function(fn)
{
  var ns = {};
  namespaces.push(fn, ns);
  return ns;
};

執行已注冊命名空間的函數,不可以命名為apply,別的什麼名字都可以。

this.initialize = function() {
  for (var i = 0; i < namespaces.length; i += 2) {
      var fn = namespaces[i];
      var ns = namespaces[i + 1];
      fn.apply(ns);
  }};

現在,然我們把前面的代碼連起來,看看全局擴展對象是如何定義和初始化的。

網頁制作Webjx文章簡介:全局變量是魔鬼,這句話在JavaScript存在的地方應該就是成立的,當然Firefox擴展也不例外.

下面這些代碼是browserOverlay.js文件的內容,這個腳本文件會在一個界面文件(browserOverlay.xul)中被引用。

// 擴展對應的唯一全局變量
var myExtension = {};
(function() { // 注冊命名空間
  var namespaces = [];
  this.ns = function(fn) {
      var ns = {};
      namespaces.push(fn, ns);
      return ns;
  };

  // 初始化
  this.initialize = function() {
      for (var i = 0; i < namespaces.length; i += 2) {
          var fn = namespaces[i];
          var ns = namespaces[i + 1];
          fn.apply(ns);
      }
  };

  // 收尾的清理工作
  this.shutdown = function() {
      window.removeEventListener("load", myExtension.initialize, false);
      window.removeEventListener("unload", myExtension.shutdown, false);
  };

  // 注冊兩個事件處理程序,維護擴展的生存期
  window.addEventListener("load", myExtension.initialize, false);
  window.addEventListener("unload", myExtension.shutdown, false);
}).apply(myExtension);

正如我前文所述,這裡只有一個全局對象myExtension。

總結一下,這個對象要實現下面幾個方法:

  • ns - 注冊一個新的命名空間。
  • initialize - 初始化所有的命名空間。
  • shutdown - 收尾的清理工作。

當然這段代碼也會確保initialize和shutdown方法會在正確的時間被調用,這也是兩個事件處理程序的作用。

browserOverlay.xul現在看起來可能會是下面這個樣子:

<?xml version="1.0"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/
there.is.only.xul">
  <script src="chrome://namespace/content/browserOverlay.js" type="application/x-javascript"/>
  <script src="chrome://namespace/content/Module1.js" type="application/x-javascript"/>
  <script src="chrome://namespace/content/Module2.js" type="application/x-javascript"/>
</overlay>

在這裡,Module1.js和Module2.js兩個文件是一模一樣的。

myExtension.ns(function() {
  // TODO: 腳本內的全部代碼
});

在不同的模塊間共享數據

我們已經把所有的腳本置於本地的作用域下,現在讓我們來回答上面提到的第二個問題,就是在不同的命名空間下如何共享函數和數據。基本的思路當然是要利用我們唯一的全局對象啦,也就是myExtension。

首先,讓我們先來看看下面這段代碼(都在lib.js文件中)

myExtension.LIB = {
  // 共享函數接口
  getCurrentURI: function() {
      return window.location.href;
  },

  // 擴展對象的快捷方式
  theApp: myExtension,

  // XPCOM組件的快捷方式
  Cc: Components.classes,
  Ci: Components.interfaces,

  // 等等。。。
};

你可以注意到,這段代碼在全局的myExtension對象下建立了一個新的LIB屬性,這個屬性定義了一個函數庫,是要在擴展所有的模塊中共享的。你應該在Java的包結構中看到過相同的做法,所有的命名空間呈樹狀結構分布在一個唯一的對象下面,YUI也是這樣子做的。

lib.js文件也在browserOverlay.xul中引入,緊隨browserOverlay.js的後面。

<?xml version="1.0"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/
there.is.only.xul">
  <script src="chrome://myextension/content/browserOverlay.js" type="application/x-javascript"/>
  <script src="chrome://myextension/content/lib.js" type="application/x-javascript"/>
  <script src="chrome://myextension/content/Module1.js" type="application/x-javascript"/>
  <script src="chrome://myextension/content/Module2.js" type="application/x-javascript"/>
</overlay>

讓我們對模塊內的腳本也做一些改進。

myExtension.ns(function() {
  with(myExtension.LIB) {
      // TODO: 腳本內的全部代碼
      var moduleVariable = "Accessible only from withing this module";
      dump("myExtension.Module initialization " + getCurrentURI() + "\n");
  }
});

通過利用with語句,我們可以方便的訪問所有的庫函數,就像訪問全局變量一樣。

既然我們要訪問全局對象,還可以像下面這樣利用theApp這個快捷方式(尤其是命名空間名字太長的時候)

myExtension.ns(function() {
  with(myExtension.LIB) {
      // TODO: 腳本內的全部代碼
      theApp.sharedValue = "A new shared property";
  }
});

下面這個圖是從UML的角度來縱觀整個架構。

大家可以在 這裡 下載本文提到的演示擴展。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved