今天看了 Join Resig's 的 “Simple JavaScript Inheritance ” 里面主要是這一句讓我我很費解.
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" 如果它們使用, 則執行一些特殊方法. 反之正常. 這個特殊方法是為了避免在 父類與子類中同時出現同一個方法. 父類將會被覆蓋.
this.Class = function(){};
Class.extend = function(prop) { // ... }
加入 extends 方法和一個簡單的 prop(一個對象) 參數. 它將返回 新構造方法的原型 + 父對象的原型;
var _super = this.prototype;
將當前對象的原型對象存儲在 _super中. this.prototype是被擴展對象的原型, 它可以訪問父級方法在你需要的地方, 這個變量叫什么 _super , 是因為 super 是保留字. 盡管現在還沒有應用起來.
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() { // special handling for _super }; })(name, prop[name]) : prop[name];
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]; }
// special handling for super var tmp = this._super; this._super = _super[name]; var ret = fn.apply(this, arguments); this._super = tmp; return ret;
var Foo = Class.extend({ qux: function() { return "Foo.qux"; } }); var Bar = Foo.extend({ qux: function() { return "Bar.qux, " + this._super(); } });
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.prototype = prototype;
Class.constructor = Class;
Class.extend = arguments.callee;
Class.extend = extend;. return Class;