首頁  >  文章  >  web前端  >  解析John Resig Simple JavaScript Inheritance程式碼_javascript技巧

解析John Resig Simple JavaScript Inheritance程式碼_javascript技巧

WBOY
WBOY原創
2016-05-16 17:47:36997瀏覽

由於作者翻譯會加入自己的理解以便自己學習和使用, 如果英文好的同學可看下面   如文章中有翻譯錯誤還請留言. 交流並改正. (:
====== ================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(St. function(){xyz;}) ? /b_superb/ : /.*/;
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(){ // }) ();

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

複製程式碼 代碼如下:
    >
這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_superb/ : /.*/;

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

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

        瀏覽器不支援Function serialisation 將會始終返回true, 那麼會始終對_super 進行額外的操作, 導致這些新的方法不能在_super 中使用. 這會有一些小的性能消耗. 但能保證在所有瀏覽器中正常執行.

複製程式碼 程式碼如下:

this.Class = function(){};

        建立一個空的構造方法, 放到全域變數中. 這將會是最上層的建構方法. 它沒有定義內容, 或一個原型物件. 除了下面的extends 方法. this 指的是window物件. 使Class 變數為全域物件.

複製程式碼 程式碼如下:
    prop) { // ...}

        加入 extends 方法與簡單的 prop(一個物件) 參數. 它將傳回 新建構方法的原型 父物件的原型; 

複製程式碼 程式碼如下:
var _super = this.prototype; >

        將目前物件的原型物件儲存在_super中. this.prototype是被擴充物件的原型, 它可以存取父級方法在你需要的地方,  這個變數叫什麼_super , 是因為super 是保留字. 儘管現在還沒有應用起來.

複製程式碼 程式碼如下:initializing = truevar prototype = newvar(this(this); initializing = false;


        實例class 物件儲存在prototype 變數中, 但不執行init 方法. 先前設定initializing 為true 所以在new Class的時候不會fire init 方法. prototype變數正常運作. (e.g 當想要建立一個真正的實例的時候)


複製程式碼 程式碼如下:
程式碼如下:


程式碼如下:


        使用一個for 循環, 我們迭代出prop 裡的屬性和方法. 該屬性是透過extends 方法傳遞進來的, 除了一些對_super 的特殊處理, 我們將值賦給prototype 屬性.


複製程式碼 程式碼如下:    prototype[name] = typeof prop[name] == "function" & type _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)
我們將會加入新的方法來處理綁定到父類新的方法以及原始方法.
        以上方式代碼看起來可能很有些混亂下面改使用一種清晰的方式查看一下.

複製程式碼 程式碼如下:
   " && 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 = special handling for supervar tmp = this._super;this._super = _supa; , 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._sup_super = function () { var tmp = this._super; this._sup_super = Foo.proto (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 = armentsoments.
        將賦其自身, 透過  arguments.callee, 在本例中表示「自身」 其實這裡我們可以避免使用arguments.callee , 如果我們修改一下我的原生方法(e.g Class.extend = function extend(prop) ) 之後我們就可以使用


複製程式碼 程式碼如下: end ;.return Class;

        實例之後會回傳, 一個原型物件, 一個建構屬性, 一個 extend 方法 與一個可自執行的 方法 init.!!!
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn