理解John Resig's 'Simple JavaScript Inheritance'代碼


  今天看了 Join Resig's 的  “Simple JavaScript Inheritance ” 里面主要是這一句讓我我很費解.

            fnTest =  /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
       
  上網也查了一下對些的理解說的都不是很清楚. 在翻閱的同時找到了一篇 分析這篇文章的文章 哈哈 分析的很詳細. (Join Resig 大師的 "Simple Inheritance" 使用了很多有意思的技巧) 如果你有時間, 並對此感興趣不訪好好看看. 我相信多少會有所收益的.
 
由於作者翻譯會加入 自己的理解 以便自己學習和使用, 如果英文好的同學可看下面   如文章中有翻譯錯誤還請留言. 交流並改正. (:
======================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 super
var 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.!!!
 
======================Enein翻譯=========================
由於作者翻譯會加入 自己的理解 以便自己學習和使用. 如有轉載請注明出處謝謝.  如文章中有翻譯錯誤或有更好的方案還請留言. 交流並改正. (:
        
        


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM