DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> 關於JavaScript >> 解析John Resig Simple JavaScript Inheritance代碼
解析John Resig Simple JavaScript Inheritance代碼
編輯:關於JavaScript     

由於作者翻譯會加入 自己的理解 以便自己學習和使用, 如果英文好的同學可看下面   如文章中有翻譯錯誤還請留言. 交流並改正. (:
======================Enein翻譯=========================

        John Resig 寫了一篇關於 JavaScript 裡 類似其它語言的 "繼承", 靈感來自於  base2 and PrototypeJS.  他為文章起名為"Simple JavaScript Inheritance" . 他使用的一些很巧妙的技術來實現 super 方法.
        你還可以看原文也會有詳細的說明, 他也在他的 "Secrets of a JavaScript Ninja"裡有所介紹. 在書中可能方法有一些不同, 它在Object中加入了subClass 方法, 而不是創建一個全局變量.
Original Script - John Resig Simple JavaScript Inheritance
下面是原諒代碼, 我移除了一些注釋使用它看起來更清晰.
復制代碼 代碼如下:
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
this.Class = function(){};
Class.extend = function(prop) {
var _super = this.prototype;
initializing = true;
var prototype = new this();
initializing = false;
for (var name in prop) {
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
function Class() {
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
Class.prototype = prototype;
Class.constructor = Class;
Class.extend = arguments.callee;
return Class;
};
})();

Breakdown of the Simple Inheritance script
下面我們來分析一下, 它是如何實現和有哪些技術被使用.

復制代碼 代碼如下:
(function(){ // ... })();

首先我們創建一個自執行匿名函數, 為代碼創建一個作用域.

復制代碼 代碼如下:     
var initializing = false

這 initializing 變量意思很直接, 它是boolean來檢查Class Function(稍後介紹)什麼時候被調用. 在創建實例時設置 initializing 為true/false 或者只是返回一個對象指向當前的原型鏈上來達到"繼承"的目的.

如果我們創建一個實例(initializing == false), 正好Class有一個init方法, 這樣 init 會自動執行。 再或者, 如果我們僅僅將它分配給原型上(initializing == true), 將不會發生什麼, init 方法不會被執行。這樣做是為了避免 每次調用構造方法都要執行 init 方法. (var prototype = new this());.

復制代碼 代碼如下:
fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

          這個fnTest的目的就是為了驗證 class method 中是否使用了 "_super()" 調用. 這種技術叫做 " function decompilation(函數反編譯)" 也叫做 "function serialisation(函數序列化)", Function serialisation 是在一個函數被轉換成字符串時發生的. 現在很多浏覽器都支持 toString 方法。

測試 Function serialisation, fnTest 使用一個匿名函數 funciton(){xyz;} 設置內容為 "xyz", 在轉變成字符串後使用正則對 "xyz" 進行查找. 它將返回true (如果浏覽器支持 function serialisation) 因為 函數將轉變成字符串所以 "xyz" 也民屬於字符串的一部分. 在這個例子中 fnTest 將返回 "/\b_super\b/", 另一種則返回 "/.*/" 如果浏覽器不支持 function serialisation 則始終返回 true。(這個指的是原始代碼中的fnTest.test)使用 fnTest 正則, 和 函數序列化技術, 我們能很容易方法中是否使用了 "_super" 如果它們使用, 則執行一些特殊方法. 反之正常.  這個特殊方法是為了避免在 父類與子類中同時出現同一個方法. 父類將會被覆蓋. 

        浏覽器不支持 Function serialisation 將會始終返回 true, 那麼會始終對 _super 進行額外的操作, 導致這些新的方法不能在 _super 中使用. 這會有一些小的性能消耗. 但能保證在所有浏覽器中 正常執行.
復制代碼 代碼如下:
this.Class = function(){};

        創建一個空的構造方法, 放到全局變量中. 這將會是最上層的構造方法. 它沒有定義內容, 或一個原型對象. 除了下面的 extends 方法. this 指的是window對象. 使 Class 變量為全局對象.
復制代碼 代碼如下:     
Class.extend = function(prop) { // ...}

        加入 extends 方法和一個簡單的 prop(一個對象) 參數. 它將返回 新構造方法的原型 + 父對象的原型; 

復制代碼 代碼如下:
var _super = this.prototype;

        將當前對象的原型對象存儲在 _super中. this.prototype是被擴展對象的原型, 它可以訪問父級方法在你需要的地方,  這個變量叫什麼 _super , 是因為 super 是保留字. 盡管現在還沒有應用起來.

復制代碼 代碼如下:
initializing = true;var prototype = new this();initializing = false;

        實例 class 對象存儲在 prototype 變量中, 但不執行 init 方法. 之前設置 initializing 為 true 所以在 new Class的時候 不會 fire init 方法. prototype變量分配後, initializing 被設置回 false, 為了下一步可以正常工作. (e.g 當想要創建一個真正的實例的時候)
復制代碼 代碼如下:   
for (var name in prop) { // ...}

        使用一個 for 循環, 我們迭代出 prop 裡的屬性和方法. 該屬性是通過 extends 方法傳遞進來的, 除了一些對 _super 的特殊處理, 我們將值賦給 prototype 屬性.
復制代碼 代碼如下:   
prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn){ return function() {  // special handling for _super }; })(name, prop[name]) : prop[name];

        當我們遍歷 prop 裡的每個對象時, 如果 滿足 (typeof prop[name] == "function")  (typeof _super[name] == "function") (fnTest.test(prop[name]) == true)
我們將會加入新的方法來處理 綁定到 父類 新的方法 以及 原始方法.
        以上方式代碼 看起來可能很有些 混亂 下面改使用 一種清晰的方式查看一下.
復制代碼 代碼如下:    
if (typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name])) { prototype[name] = (function(name, fn){ return function() {  // special handling for _super }; })(name, prop[name]);} else { // just copy the property prototype[name] = prop[name];}

        另一個自執行匿名函數, 在處理 super 中的 name prop[name] 被使用 . 沒有這個閉包. 當返回這個function時 這個變量的引用將會出錯.(e.g 它始終會返回 循環的最後一個)
        遍歷所有, 我們將返回一個新的函數, 這個函數來處理 原生方法(via super) 和 新方法.

復制代碼 代碼如下:       
// special handling for supervar tmp = this._super;this._super = _super[name];var ret = fn.apply(this, arguments);this._super = tmp;return ret;

        對 super 的特殊處理, 我們首先要存儲 已存在 _super 屬性和類的一些參數. 存儲在 臨時 tmp 裡, 這是為了防止 _super 中已存在的方法被重寫
完事兒後我們將 tmp 在賦給 this._super 這樣它就可以正常工作了.
         下一步, 我們將 _super[name] 方法賦給 當前對象的 this._super, 這樣當 fn 通過 apply 被執行的時候 this._super()就會指向 父類方法, 這個
父類方法中的 this 也同樣可以訪問 當前對象.
         最後我們將返回值存儲在 ret 中, 在將 _super 設置回來後返回該對象.
        下面有個簡單的例子,  定義個簡單的 Foo , 創建繼承對象 Bar:
復制代碼 代碼如下:
var Foo = Class.extend({ qux: function() { return "Foo.qux"; }});var Bar = Foo.extend({ qux: function() { return "Bar.qux, " + this._super(); }});

         當 Foo.extends 被執行, 在 qux 方法中由於存在 this._super 所以 Bar原型上的qux 實際上應該是這樣的:
復制代碼 代碼如下:      
Bar.prototype.qux = function () { var tmp = this._super; this._super = Foo.prototype.qux; var ret = (function() { return "Bar.qux, " + this._super(); }).apply(this, arguments); this._super = tmp; return ret;}

        在腳本中完成這步後, 構造方法將被調用
復制代碼 代碼如下:
function Class() { if ( !initializing && this.init ) this.init.apply(this, arguments);}

        這段代碼調用 Class 創建一個新的構造方法, 這不同於之前創建的 this.Class, 作為本地的 Class.extend. 這個構造方法返回 Class.extend 的調用(比如之前 Foo.extends).  new Foo() 實例後這個構造方法將被執行.
        構造方法將會自動執行 init() 方法(如果存在的話) 正好上面說的那樣, 這個 initializing 變量來控制 init 是否被執行.

復制代碼 代碼如下:      
Class.prototype = prototype;

        最後這個 prototype,  從父類的構造方法返回一個混合後的 父類原型對象. (e.g var prototype = new this()), 這個結果是通過 extend 函數裡的for循環.

Class.constructor = Class;
        因為我們重寫了整個原型對象, 在這個類型中存儲這個 原生的構造方法,  讓它在一個實例的構造方法中能保持默認形為.
復制代碼 代碼如下:       
Class.extend = arguments.callee;

        將賦其自身, 通過  arguments.callee, 在本例中表示 “自身” 其實這裡我們可以 避免使用 arguments.callee , 如果我們修改一下我的原生方法(e.g Class.extend = function extend(prop)) 之後我們就可以通過 使用
復制代碼 代碼如下:       
Class.extend = extend;.return Class;

        實例之後會返回, 一個原型對象, 一個構造屬性, 一個 extend 方法 和一個可自執行的 方法 init.!!!

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