国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > JS > 正文

JS應用在Firebug中的擴展架構模式

2024-09-06 12:40:55
字體:
來源:轉載
供稿:網友

全局變量是魔鬼,這句話在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);
  }};

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

|||

下面這些代碼是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的角度來縱觀整個架構。

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 通河县| 西吉县| 双辽市| 天津市| 江达县| 德江县| 河间市| 木兰县| 桓台县| 师宗县| 大城县| 广水市| 乡宁县| 蒙阴县| 三门峡市| 铜川市| 乌兰浩特市| 怀仁县| 桃园县| 花莲市| 井冈山市| 浠水县| 明水县| 伊宁县| 冷水江市| 天台县| 屏东县| 漳平市| 临高县| 井陉县| 南城县| 西安市| 临夏县| 伽师县| 北川| 永安市| 辽中县| 新沂市| 华容县| 华容县| 汝南县|