<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 495,comments - 227,trackbacks - 0

    一,背景

    回顧一下編程語言的發展,不難發現這是一個不斷封裝的過程:從最開始的匯編語言,到面向過程語言,然后到面向對象語言,再到具備面向對象特性的腳本語言,一層一層封裝,一步一步減輕程序員的負擔,逐漸提高編寫程序的效率。這篇文章是關于 JavaScript 的,所以我們先來了解一下 JavaScript 是一種怎樣的語言。到目前為止,JavaScript 是一種不完全支持面向對象特性的腳本語言。之所以這樣說是因為 JavaScript 的確支持對象的概念,在程序中我們看到都是對象,可是 Javascipt 并不支持類的封裝和繼承。曾經有過 C++Java或者 phppython 編程經驗的讀者都會知道,這些語言允許我們使用類來設計對象,并且這些類是可繼承的。JavaScript 的確支持自定義對象和繼承,不過使用的是另外一種方式:prototype(中文譯作:原型)。用過 JavaScript 的或者讀過《設計模式》的讀者都會了解這種技術,描述如下:

    每個對象都包含一個 prototype 對象,當向對象查詢一個屬性或者請求一個方法的時候,運行環境會先在當前對象中查找,如果查找失敗則查找其 prototype 對象。注意 prototype 也是一個對象,于是這種查找過程同樣適用在對象的 prototype 對象中,直到當前對象的 prototpye 為空。

    JavaScript 中,對象的 prototype 在運行期是不可見的,只能在定義對象的構造函數時,創建對象之前設定。下面的用法都是錯誤的:

    o2.prototype = o1;

    /*

    這時只定義了 o2 的一個名為“prototype”的屬性,

    并沒有將 o1 設為 o2 prototype

    */

    // ---------------

    f2 = function(){};

    o2 = new f2;

    f2.prototype = o1;

    /*

    這時 o1 并沒有成為 o2 prototype

    因為 o2 f2 設定 prototype 之前已經被創建。

    */

    // ---------------

    f1 = function(){};

    f2 = function(){};

    o1 = new f1;

    f2.prototype = o1;

    o2 = new f2;

    /*

    同樣,這時 o1 并不是 o2 prototype

    因為 JavaScript 不允許構造函數的 prototype 對象被其它變量直接引用。

    */

    正確的用法應該是:

    f1 = function(){};

    f2 = function(){};

    f2.prototype = new f1;

    o2 = new f2;

    從上面的例子可以看出:如果你想讓構造函數 F2 繼承另外一個構造函數 F1 所定義的屬性和方法,那么你必須先創建一個 F1 的實例對象,并立刻將其設為 F2 prototype。于是你會發現使用 prototype 這種繼承方法實際上是不鼓勵使用繼承:一方面是由于 JavaScript 被設計成一種嵌入式腳本語言,比方說嵌入到瀏覽器中,用它編寫的應用一般不會很大很復雜,不需要用到繼承;另一方面如果繼承得比較深,prototype 鏈就會比較長,用在查找對象屬性和方法的時間就會變長,降低程序的整體運行效率。

    二,問題

    現在 JavaScript 的使用場合越來越多,web2.0 有一個很重要的方面就是用戶體驗。好的用戶體驗不但要求美工做得好,并且講求響應速度和動態效果。很多有名的 web2.0 應用都使用了大量的 JavaScript 代碼,比方說 FlickrGmail等等。甚至有些人用 Javasript 來編寫基于瀏覽器的 GUI,比方說 BackbaseQooxdoo等等。于是 JavaScript 代碼的開發和維護成了一個很重要的問題。很多人都不喜歡自己發明輪子,他們希望 JavaScript 可以像其它編程語言一樣,有一套成熟穩定 Javasript 庫來提高他們的開發速度和效率。更多人希望的是,自己所寫的 JavaScript 代碼能夠像其它面向對象語言寫的代碼一樣,具有很好的模塊化特性和很好的重用性,這樣維護起來會更方便。可是現在的 JavaScript 并沒有很好的支持這些需求,大部分開發都要重頭開始,并且維護起來很不方便。

    三,已有解決方案

    有需求自然就會有解決方案,比較成熟的有兩種:

    1 ,現在很多人在自己的項目中使用一套叫 prototype.js JavaScript 庫,那是由 MVC web 框架 Ruby on Rails開發并使用 JavaScript 基礎庫。這套庫設計精良并且具有很好的可重用性和跨瀏覽器特性,使用 prototype.js 可以大大簡化客戶端代碼的開發工作。prototype.js 引入了類的概念,用其編寫的類可以定義一個 initialize 的初始化函數,在創建類實例的時候會首先調用這個初始化函數。正如其名字,prototype.js 的核心還是 prototype,雖然提供了很多可復用的代碼,但沒有從根本上解決 JavaScript 的開發和維護問題。

    2 ,使用 asp.net 的人一般都會聽過或者用到一個叫 Atlas的框架,那是微軟的 AJAX 利器。Atlas 允許客戶端代碼用類的方法來編寫,并且比 prototype.js 具備更好的面向對象特性,比方說定義類的私有屬性和私有方法、支持繼承、像java那樣編寫接口等等。Atlas 是一個從客戶端到服務端的解決方案,但只能在 asp.net 中使用、版權等問題限制了其使用范圍。

    從根本上解決問題只有一個,就是等待 JavaScript2.0(或者說ECMAScript4.0)標準的出臺。在下一版本的 JavaScript 中已經從語言上具備面向對象的特性。另外,微軟的 JScript.NET已經可以使用這些特性。當然,等待不是一個明智的方法。

    四,Modello 框架

    如果上面的表述讓你覺得有點頭暈,最好不要急于了解 Modello 框架,先保證這幾個概念你已經能夠準確理解:

    • JavaScript 構造函數:在 JavaScript 中,自定義對象通過構造函數來設計。運算符 new 加上構造函數就會創建一個實例對象
    • JavaScript 中的 prototype:如果將一個對象 P 設定為一個構造函數 F prototype,那么使用 F 創建的實例對象就會繼承 P 的屬性和方法
    • 類:面向對象語言使用類來封裝和設計對象。按類型分,類的成員分為屬性和方法。按訪問權限分,類的成員分為靜態成員,私有成員,保護成員,公有成員
    • 類的繼承:面向對象語言允許一個類繼承另外一個類的屬性和方法,繼承的類叫做子類,被繼承的類叫做父類。某些語言允許一個子類只能繼承一個父類(單繼承),某些語言則允許繼承多個(多繼承)
    • JavaScript 中的 closure 特性:函數的作用域就是一個 closureJavaScript 允許在函數 O 中定義內部函數 I ,內部函數 I 總是可以訪問其外部函數 O 中定義的變量。即使在外部函數 O 返回之后,你再調用內部函數 I ,同樣可以訪問外部函數 O 中定義的變量。也就是說,如果你在構造函數 C 中用 var 定義了一個變量V,用 this 定義了一個函數F,由 C 創建的實例對象 O 調用 O.F 時,F 總是可以訪問到 V,但是用 O.V 這樣來訪問卻不行,因為 V 不是用 this 來定義的。換言之,V 成了 O 的私有成員。這個特性非常重要,如果你還沒有徹底搞懂,請參考這篇文章Private Members in JavaScript

    搞懂上面的概念,理解下面的內容對你來說已經沒有難度,開始吧!

    如題,Modello 是一個允許并且鼓勵你用 JavaScript 來編寫類的框架。傳統的 JavaScript 使用構造函數來自定義對象,用 prototype 來實現繼承。在 Modello 中,你可以忘掉晦澀的 prototype,因為 Modello 使用類來設計對象,用類來實現繼承,就像其它面向對象語言一樣,并且使用起來更加簡單。不信嗎?請繼續往下看。

    使用 Modello 編寫的類所具備如下特性:

    • 私有成員、公共成員和靜態成員
    • 類的繼承,多繼承
    • 命名空間
    • 類型鑒別

    Modello 還具有以下特性:

    • 更少的概念,更方便的使用方法
    • 小巧,只有兩百行左右的代碼
    • 設計期和運行期徹底分離,使用繼承的時候不需要使用 prototype,也不需要先創建父類的實例
    • 兼容 prototype.js 的類,兼容 JavaScript 構造函數
    • 跨瀏覽器,跨瀏覽器版本
    • 開放源代碼,BSD licenced,允許免費使用在個人項目或者商業項目中

    下面介紹 Modello 的使用方法:

    1 ,定義一個類

    Point = Class.create();

    /*

    創建一個類。用過 prototype.js 的人覺得很熟悉吧;)

    */

    2 ,注冊一個類

    Point.register("Modello.Point");

    /*

    這里"Modello"是命名空間,"Point"是類名,之間用"."分隔

    如果注冊成功,

    Point.namespace 等于 "Modello"Point.classname 等于 "Point"

    如果失敗 Modello 會拋出一個異常,說明失敗原因。

    */

    Point.register("Point"); // 這里使用默認的命名空間 "std"

    Class.register(Point, "Point"); // 使用 Class register 方法

    3 ,獲取已注冊的類

    P = Class.get("Modello.Point");

    P = Class.get("Point"); // 這里使用默認的命名空間 "std"

    4 ,使用繼承

    ZPoint = Class.create(Point); // ZPoint 繼承 Point

    ZPoint = Class.create("Modello.Point"); // 繼承已注冊的類

    ZPoint = Class.create(Point1, Point2[, ...]);

    /*

    多繼承。參數中的類也可以用已注冊的類名來代替

    */

    /*

    繼承關系:

    Point.subclasses 內容為 [ ZPoint ]

    ZPoint.superclasses 內容為 [ Point ]

    */

    5 ,定義類的靜態成員

    Point.count = 0;

    Point.add = function(x, y) {

    return x + y;

    }

    6 ,定義類的構造函數

    Point.construct = function($self, $class) {

    // "var" 來定義私有成員

    var _name = "";

    var _getName = function () {

    return _name;

    }

    // "this" 來定義公有成員

    this.x = 0;

    this.y = 0;

    this.initialize = function (x, y) { // 初始化函數

    this.x = x;

    this.y = y;

    $class.count += 1; // 訪問靜態成員

    // 公有方法訪問私有私有屬性

    this.setName = function (name) {

    _name = name;

    }

    this.getName = function () {

    return _getName();

    }

    this.toString = function () {

    return "Point(" + this.x + ", " + this.y + ")";

    }

    // 注意:initialize toString 方法只有定義成公有成員才生效

    this.add = function() {

    // 調用靜態方法,使用構造函數傳入的 $class

    return $class.add(this.x, this.y);

    }

    }

    ZPoint.construct = function($self, $class) {

    this.z = 0; // this.x, this.y 繼承自 Point

    // 重載 Point 的初始化函數

    this.initialize = function (x, y, z) {

    this.z = z;

    // 調用第一個父類的初始化函數,

    // 第二個父類是 $self.super1,如此類推。

    // 注意:這里使用的是構造函數傳入的 $self 變量

    $self.super0.initialize.call(this, x, y);

    // 調用父類的任何方法都可以使用這種方式,但只限于父類的公有方法

    }

    // 重載 Point toString 方法

    this.toString = function () {

    return "Point(" + this.x + ", " + this.y +

    ", " + this.z + ")";

    }

    }

    // 連寫技巧

    Class.create().register("Modello.Point").construct = function($self, $class) {

    // ...

    }

    7 ,創建類的實例

    // 兩種方法:new create

    point = new Point(1, 2);

    point = Point.create(1, 2);

    point = Class.get("Modello.Point").create(1, 2);

    zpoint = new ZPoint(1, 2, 3);

    8 ,類型鑒別

    ZPoint.subclassOf(Point); // 返回 true

    point.instanceOf(Point); // 返回 true

    point.isA(Point); // 返回 true

    zpoint.isA(Point); // 返回 true

    zpoint.instanceOf(Point); // 返回 false

    // 上面的類均可替換成已注冊的類名

    以上就是 Modello 提供的全部功能。下面說說使用 Modello 的注意事項和建議:

    • 在使用繼承時,傳入的父類可以是使用 prototype.js 方式定義的類或者 JavaScript 方式定義的構造函數
    • 類實際上也是一個函數,普通的 prototype 的繼承方式同樣適用在用 Modello 定義的類中
    • 類可以不注冊,這種類叫做匿名類,不能通過 Class.get 方法獲取
    • 如果定義類構造函數時,像上面例子那樣提供了 $self, $class 兩個參數,Modello 會在創建實例時將實例本身傳給 $self,將類本身傳給 $class$self 一般在訪問父類成員時才使用,$class 一般在訪問靜態成員時才使用。雖然 $self$class 功能很強大,但不建議你在其它場合使用,除非你已經讀懂 Modello 的源代碼,并且的確有特殊需求。更加不要嘗試使用 $self 代替 this,這樣可能會給你帶來麻煩
    • 子類無法訪問父類的私有成員,靜態方法中無法訪問私有成員
    • Modello 中私有成員的名稱沒有特別限制,不過用"_"開始是一個好習慣
    • Modello 不支持保護(protected)成員,如果你想父類成員可以被子類訪問,則必須將父類成員定義為公有。你也可以參考 "this._property" 這樣的命名方式來表示保護成員:)
    • 盡量將一些輔助性的計算復雜度大的方法定義成靜態成員,這樣可以提高運行效率
    • 使用 Modello 的繼承和類型鑒別可以實現基本的接口(interface)功能,你已經發現這一點了吧;)
    • 使用多繼承的時候,左邊的父類優先級高于右邊的父類。也就是說假如多個父類定義了同一個方法,最左邊的父類定義的方法最終被繼承

    使用 Modello 編寫的類功能可以媲美使用 Atlas 編寫的類,并且使用起來更簡潔。如果你想用 Modello 框架代替 prototype.js 中的簡單類框架,只需要先包含 modello.js,然后去掉 prototype.js 中定義 Class 的幾行代碼即可,一切將正常運行。

    ?

    如果你發現 Modello bug,非常歡迎你通過 email聯系我。如果你覺得 Modello 應該具備更多功能,你可以嘗試閱讀一下源代碼,你會發現 Modello 可以輕松擴展出你所需要的功能。

    Modello 的原意為大型藝術作品的模型,希望 Modello 能夠幫助你編寫高質量的 JavaScript 代碼。

    5 ,下載

    Modello 的完整參考說明和下載地址:http://modello.sourceforge.net

    ?

    posted on 2006-10-26 18:52 SIMONE 閱讀(294) 評論(0)  編輯  收藏 所屬分類: JavaScript
    主站蜘蛛池模板: 国产亚洲免费的视频看| 亚洲AV成人影视在线观看| 亚洲一区二区三区丝袜| 91久久精品国产免费直播| 亚洲综合色婷婷七月丁香| 羞羞网站在线免费观看| 日韩欧毛片免费视频 | 国产成人综合亚洲亚洲国产第一页| 久久精品国产亚洲AV忘忧草18| 免费视频成人手机在线观看网址| 国产成人亚洲综合无码| 污视频网站在线观看免费| 日韩电影免费在线| 亚洲成AV人影片在线观看| 100000免费啪啪18免进| 亚洲伊人久久精品| 免费精品人在线二线三线区别 | 亚洲日本天堂在线| 免费99热在线观看| 一级免费黄色大片| 亚洲欧洲国产日韩精品| 1000部国产成人免费视频| 亚洲伊人久久大香线蕉在观| 成人性生交视频免费观看| 精品国产亚洲第一区二区三区| 国产免费观看视频| 久草免费福利视频| 亚洲精品无码永久在线观看男男 | 亚洲午夜电影一区二区三区| 亚洲美女视频免费| 国产精品无码亚洲一区二区三区| 亚洲美女在线国产| 91精品免费久久久久久久久| 亚洲精品无码一区二区| 亚洲国产精品久久久天堂| 成年美女黄网站色大免费视频| 精品国产呦系列在线观看免费| 亚洲福利电影在线观看| 久久精品国产精品亚洲人人| 亚洲免费二区三区| 成全在线观看免费观看大全|