大家好!今天跟大家一起分享我的OOP框架YOOP重構實踐,希望能與大家一起共同學習、共同進步。
本文展示了我對沒有編寫測試的YOOP原始版本的重構過程。通過該重構,力圖將一個雜亂無章的遺留代碼重構為有良好測試的、結構良好、可讀性較強的高質量代碼。
在本次重構中,我不但會對代碼結構進行重構,還會改變部分行為(如將“抽象類要檢查是否實現了接口成員和父類的抽象方法”修改為“抽象類不檢查是否實現了接口成員和父類的抽象方法”;將“抽象類、類只能繼承1個接口”修改為“可以繼承多個接口”等等)。改變行為時,必須先添加或者修改測試,然后才能小步地改變行為。
原始版本

(function () { /************************************************** String對象擴展 *********************************************************** 擴展方法: contain containIgnoreCase trim */ if (!String.prototype.contain) { String.prototype.contain = function (str) { /* 使用RegExp對象來構造動態匹配。 注意!str是字符串,因此需要轉義! 由於JavaScript字符串中的“\”是一個轉義字符,因此,使用顯式構造函數創建RegExp實例對象時,應將原始正則表達式中的“\”用“\\”替換。例如,在代碼1.2中的兩條語句是等價的。 代碼1.2 轉義字符中的“\”:1.2.htm <script language="javascript"> var re1 = new RegExp("\\d{5}"); var re2 = /\d{5}/; alert("re1="+re1+"\nre2="+re2); </script> 由於正則表達式模式文本中的轉義字符也是“\”,如果正則表達式中要匹配原義字符“\”,在正則表達式模式文本中要以“\\”來表示,當使用顯式構造函數的方式創建RegExp實例對象的時候,就需要使用“\\\\”來表示原義字符“\”。 var re = new RegExp(\\\\)。 */ var reg = new RegExp(str); if (this.match(reg)) { //用this指針指代本體 return true; } else { return false; } } } /*****************************************************************************************************************************/ //當前是否處於創建類的階段。 //放在自執行函數中,initializing就是自執行函數的內部變量,自執行函數的上下文結束后,外部就不能訪問initializing了。 //不用var的話,就不是當前上下文的一個變量了,而是全局對象的一個屬性。這樣外部就能夠訪問了。 var initializing = false; // var count = 0; //獲得函數的參數數組 function argumentNames(fn) { var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g, '').split(','); return names.length == 1 && !names[0] ? [] : names; }; /* 深拷貝 */ function extendDeep(parent, child) { var i, toStr = Object.prototype.toString, sArr = "[object Array]", sOb = "[object Object]", type = "", child = child || {}; for (i in parent) { //if (parent.hasOwnProperty && parent.hasOwnProperty(i)) { // if (typeof parent[i] === 'object') { //null === 'object'也為true! type = toStr.call(parent[i]); if (type === sArr || type === sOb) { //如果為數組或object對象 child[i] = type === sArr ? [] : {}; extendDeep(parent[i], child[i]); } else { child[i] = parent[i]; } } //} return child; }; //獲得函數名 function getFunctionName(fn) { var name = ""; if (!fn) { return null; } name = fn.toString().match(/^.*function\s*([^\(]*)/); return name === null ? name : name[1]; }; //判斷是否為數組 function isArray(val) { return Object.prototype.toString.call(val) === "[object Array]"; }; //檢查抽象類的公有方法+虛方法+抽象方法是否包含父類的抽象方法/屬性 或 接口方法/屬性。 //不用hasOwnProperty判斷!否則就檢查不到是否包含了父類的抽象方法/屬性 或 接口方法/屬性。 function check(parentClass, interface, children) { // if (!parent || !interface || !children) { // throw new Error("check - arguments error!"); // } var name = ""; if (parentClass) { //檢查是否實現了抽象方法/屬性 for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { // console.log(name); if (name === "constructor") { continue; } if (name.contain("Abstract_")) { //抽象方法 if (typeof parentClass.prototype[name] === "function") { if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] !== "function") { // var t = name.slice(9); throw new Error("Abstract method '" + name + "' must be overwrited!"); } } //抽象屬性 else { if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] === "function") { // var t = name.slice(9); // var p = children[name.slice(9)]; // var q = typeof children[name.slice(9)]; throw new Error("Abstract attribute '" + name + "' must be overwrited!"); } } } } } } if (!interface) { return; } //檢查是否實現了接口方法/屬性 for (name in interface.prototype) { // console.log(name); if (name === "constructor") { continue; } // if (interface.prototype.hasOwnProperty(name)) { //接口方法 if (typeof interface.prototype[name] === "function") { // var t = name.slice(10); // var m = children[name.slice(10)]; // console.log("t = " + t); // console.log("m = " + m); if (children[name.slice(10)] === undefined || typeof children[name.slice(10)] !== "function") { throw new Error("Interface method '" + name + "' must be overwrited!"); } } //接口屬性 else { // var t = name.slice(10); // var m = children[name.slice(10)]; // console.log("t = " + t); // console.log("m = " + m); if (children[name.slice(10)] === undefined || typeof children[name.slice(10)] === "function") { throw new Error("Interface attribute '" + name + "' must be overwrited!"); } } // } } }; //檢查抽象成員 function addAbstract(abstract, currentClass, temp) { var name = ""; for (name in abstract) { if (abstract.hasOwnProperty(name)) { // if (typeof abstract[name] !== "function") { // throw new Error("Virtual attribute is not allowed!"); // } // else { //抽象方法前面加"Abstract_"前綴 currentClass.prototype["Abstract_" + name] = abstract[name]; // currentClass.prototype[name] = abstract[name]; temp[name] = abstract[name]; //加入temp // } } } }; //檢查虛方法(不能為虛屬性) function addVirtual(virtual, currentClass, temp) { var name = ""; for (name in virtual) { if (virtual.hasOwnProperty(name)) { if (typeof virtual[name] !== "function") { throw new Error("Virtual attribute is not allowed!"); } else { currentClass.prototype[name] = virtual[name]; temp[name] = virtual[name]; //加入temp } } } }; //加入密封方法。 //沒有實現檢查子類是否重寫了父類的密封方法,只是定義了一個規范。 function addSealed(sealed, currentClass, temp) { var name = ""; for (name in sealed) { if (sealed.hasOwnProperty(name)) { currentClass.prototype[name] = sealed[name]; temp[name] = sealed[name]; //加入temp } } }; //獲得在原型prototype中不存在同名的str。 //如果有同名,則加上前綴"_" function getNoRepeatStrInPrototype(prototype, str) { var new_str = ""; if (!prototype[str]) { return str; } new_str = "_" + str; return getNoRepeatStrInPrototype(prototype, new_str); } //創建接口 //接口可以繼承接口 function MyInterface(_parent, _method, _attribute) { var i = 0, args = null; var parent = null, method = null, attribute = null; if (typeof _parent === "function") { if (getFunctionName(_parent) !== "I") { throw new Error("Interface must inherit interface!"); } else { parent = _parent; //形如“MyInterface(Parent, "A", "B", "GetName");” if (_method && !isArray(_method)) { method = Array.prototype.slice.call(arguments, 1); attribute = null; } //形如“MyInterface(Parent, ["A", "B", "GetName"], ["a", "c"]);” else { method = _method; attribute = _attribute; } } // console.log(parent.toString()); } else { parent = null; //形如“MyInterface("A", "B", "GetName");” if (_method && !isArray(_method)) { method = arguments attribute = null; } //形如“MyInterface(["A", "B", "GetName"], ["a", "c"]);” else { method = arguments[0]; attribute = arguments[1]; } } function I() { } // 如果此接口需要從其它接口擴展 if (parent) { I.prototype = new parent(); I.prototype.constructor = I; } // console.log("method = " + method); // console.log("attribute = " + attribute); // //形如“MyInterface(["A", "B", "GetName"], ["a", "c"]);” // if (isArray(method)) { //方法 for (i = 0; i < method.length; i++) { //加上前綴“Interface_” I.prototype["Interface_" + method[i]] = function () { throw new Error("This method must be overwrited!"); }; } //屬性 if (attribute) { if (!isArray(attribute)) { throw new Error("Attribute must be array!"); } else { for (i = 0; i < attribute.length; i++) { //加上前綴“Interface_” I.prototype["Interface_" + attribute[i]] = 0; } } } // } // //形如“MyInterface("A", "B", "GetName");” // else { // args = Array.prototype.slice.call(arguments, 1); // //方法 // for (i = 0; i < args.length; i++) { // I.prototype[args[i]] = function () { // throw new Error("This method must be overwrited!"); // }; // } // } return I; }; //創建抽象類 //抽象類能夠繼承接口、抽象類以及實體類,但此處約定抽象類只能繼承接口和抽象類,不能繼承實體類! //(這樣方便判斷抽象類是否包含全部的父類(接口/抽象類)成員) function MyAbstract(_parent, _prop) { var Static = null; var k = null, name = null, temp = {}, virtual = {}; // if (arguments.length > 1) { // throw new Error("AbstractClass can't inherit other classes!"); // } var abstractClass = null, interface = null, prop = null; //原型恢復標志,用於防止第一次創建實例時恢復原型 var mark_resume = false; //取出父類、接口 if (arguments.length === 1) { prop = arguments[0]; // parent = null; abstractClass = null; interface = null; } //_parent為{Class: xx, Interface: xx} else if (typeof _parent === "object") { if (!_parent.Class && !_parent.Interface) { throw new Error("Please add AbstractClass or Interface!"); } if (getFunctionName(_parent.Class) === "F" || getFunctionName(_parent.Interface) === "F") { throw new Error("AbstractClass here can't inherit parentClass which is created by MyClass function!"); } abstractClass = _parent.Class; interface = _parent.Interface; prop = _prop; } //_parent直接為xx,就表示父類為抽象類 else if (typeof _parent === "function") { if (getFunctionName(_parent) === "F") { throw new Error("AbstractClass here can't inherit parentClass which is created by MyClass function!"); } abstractClass = _parent; interface = null; prop = _prop; } else { throw new Error("arguments is not allowed!"); } Static = prop.Static ? prop.Static : null; // 本次調用所創建的類(構造函數) function A() { // // 如果抽象父類存在,則實例對象的baseClass指向父類的原型 // // 這就提供了在實例對象中調用父類方法的途徑 // if (abstractClass) { // this.baseClass = abstractClass.prototype; // } ////防止第一次創建實例時恢復原型 //if (mark_resume) { // //還原原型 // extendDeep(A.prototype.backUp_prototype, A.prototype); //} //else { // mark_resume = true; //} } // 如果此接口需要從其它接口擴展 if (abstractClass) { // //刪除父類的私有成員,保留本類的私有成員 // for (name in abstractClass.prototype) { // if (abstractClass.prototype.hasOwnProperty(name)) { // //私有成員以“_”開頭,可能有多個“_”(多層繼承) // if (!name.match(/^_+/)) { // // delete parentClass.prototype[name]; // A.prototype[name] = abstractClass.prototype[name]; // } // } // } //A.prototype = new abstractClass(); A.prototype = extendDeep(abstractClass.prototype); A.prototype.constructor = A; // 如果父類存在,則實例對象的baseClass指向父類的原型。 // 這就提供了在實例對象中調用父類方法的途徑。 //baseClass的方法是指向abstractClass的,不是指向F(子類)的! A.prototype[getNoRepeatStrInPrototype(abstractClass.prototype, "baseClass")] = abstractClass.prototype; //A.prototype.baseClass = abstractClass.prototype; } //加入構造函數 //抽象類本身因為不能實例化,所以不調用構造函數。 //抽象類中的構造函數供子類構造函數中調用。 if (prop.Init) { if (abstractClass) { A.prototype.Init = function (name) { return function () { //此處不用創建閉包了!因為外面已經創建了閉包,name已經被保存了! this.base = function () { //這個寫法也可以!為什么不用apply修正this也行??! //parentClass.prototype[name](); //此處的arguments為base方法傳入的形參 //注意!要加上“return”,這樣才能返回parentClass.prototype[name]的返回值 return abstractClass.prototype[name].apply(abstractClass.prototype, arguments); }; //指向子類,可以用於模版模式 this.baseToSubClass = abstractClass.prototype[name]; //執行fn並返回執行的結果 //此處的arguments為F.prototype[name]方法傳入的形參。 return prop[name].apply(this, arguments); }; }("Init"); } else { A.prototype.Init = prop.Init; } } if (prop.Private) { //私有屬性/方法直接覆蓋 for (name in prop.Private) { if (prop.Private.hasOwnProperty(name)) { A.prototype[name] = prop.Private[name]; } } } if (prop.Public) { for (name in prop.Public) { if (prop.Public.hasOwnProperty(name)) { //檢查抽象成員,抽象成員放到Public或Protected中 if (name === "Abstract") { addAbstract(prop["Public"][name], A, temp); continue; } //檢查虛方法,虛方法放到Public或Protected中 if (name === "Virtual") { addVirtual(prop["Public"][name], A, temp); continue; } //密封的方法(不允許子類重寫) if (name === "Sealed") { addSealed(prop["Public"][name], A, temp); continue; } if (abstractClass && typeof prop.Public[name] === "function" && typeof A.prototype[name] === "function") { A.prototype[name] = function (name) { return function () { //此處不用創建閉包了!因為外面已經創建了閉包,name已經被保存了! this.base = function () { //這個寫法也可以!為什么不用apply修正this也行??! //parentClass.prototype[name](); //此處的arguments為base方法傳入的形參 //注意!要加上“return”,這樣才能返回parentClass.prototype[name]的返回值 return abstractClass.prototype[name].apply(abstractClass.prototype, arguments); }; //指向子類,可以用於模版模式 this.baseToSubClass = abstractClass.prototype[name]; //執行fn並返回執行的結果 //此處的arguments為F.prototype[name]方法傳入的形參。 return prop.Public[name].apply(this, arguments); }; }(name); } else { A.prototype[name] = prop.Public[name]; } temp[name] = prop.Public[name]; //用於檢查是否包含父類的抽象方法/屬性 或 接口方法/屬性 } } } //保護成員 if (prop.Protected) { for (name in prop.Protected) { if (prop.Protected.hasOwnProperty(name)) { //檢查抽象成員,抽象成員放到Public或Protected中 if (name === "Abstract") { addAbstract(prop["Protected"][name], A, temp); continue; } //檢查虛方法,虛方法放到Public或Protected中 if (name === "Virtual") { addVirtual(prop["Protected"][name], A, temp); continue; } //密封的方法(不允許子類重寫) if (name === "Sealed") { addSealed(prop["Protected"][name], A, temp); continue; } A.prototype[name] = prop.Protected[name]; } } } // //虛方法(不能為虛屬性) // if (prop.Virtual) { // for (name in prop.Virtual) { // if (prop.Virtual.hasOwnProperty(name)) { // if (typeof prop.Virtual[name] !== "function") { // throw new Error("Virtual attribute is not allowed!"); // } // else { // // //虛方法前面加"Virtual_"前綴,在子類中要檢查虛方法 // A.prototype[name] = prop.Virtual[name]; // temp[name] = prop.Virtual[name]; //用於檢查是否包含父類的抽象方法/屬性 或 接口方法/屬性 // } // } // } // } //抽象類可以沒有抽象成員 // if (!prop.Abstract) { // throw new Error("AbstractClass must have abstract methods!"); // } //放到外面的抽象成員,默認為公有抽象成員 for (name in prop.Abstract) { if (prop.Abstract.hasOwnProperty(name)) { // console.log(); //抽象方法前面加"Abstract_"前綴 A.prototype["Abstract_" + name] = prop.Abstract[name]; temp[name] = prop.Abstract[name]; //用於檢查是否包含父類的抽象方法/屬性 或 接口方法/屬性 } } // //檢查抽象類的公有方法+虛方法+抽象方法是否包含父類的抽象方法/屬性 或 接口方法/屬性 //檢查抽象類的公有方法+虛方法+抽象方法是否包含父類的接口方法/屬性 check(null, interface, temp); //靜態屬性/方法賦值 for (k in Static) { A[k] = Static[k]; } ////備份原型 //A.prototype.backUp_prototype = extendDeep(A.prototype); return A; } // //是否調用父類函數 // var base_flag = false; //創建普通類 //父類_parent可以為{Class: xx, Interface: xx},或者直接為xx類 function MyClass(_parent, _prop) { // console.log("length = " + arguments.length); var Static = null; // Private = null, // Public = null, // Origin = null; var k = null, name = null; var parentClass = null, interface = null, prop = null, temp = {}; // var temp = null; // //原型備份容器,用於創建實例時,恢復最初的原型(每次創建實例時,原型都保持不變)。 // var backUp_prototype = {}; //原型恢復標志,用於防止第一次創建實例時恢復原型 var mark_resume = false; //取出父類、接口 if (arguments.length === 1) { prop = arguments[0]; parentClass = null; interface = null; } //{Class: xx, Interface: xx} else if (typeof _parent === "object") { // if (parent.Class) if (!_parent.Class && !_parent.Interface) { throw new Error("Please add Class or Interface!"); } parentClass = _parent.Class; interface = _parent.Interface; prop = _prop; } //直接為xx類 else if (typeof _parent === "function") { parentClass = _parent; interface = null; // parent = _parent; // interface = null; prop = _prop; } else { throw new Error("arguments is not allowed!"); } //取出靜態屬性/方法、私有屬性/方法、公有屬性/方法 // Private = prop.Private; // Public = prop.Public; Static = prop.Static ? prop.Static : null; // //保存原始的私有屬性,用於創建實例時,重置私有屬性 // // var Origin = {}; // // Origin = Private // Origin = operate.extendDeep(Private); // YYC.Tool.extend.extend(Origin, Private); // //訪問公共屬性/方法的入口, // MyClass.Public = Public; // 本次調用所創建的類(構造函數) function F() { // console.log(mark_resume); //防止第一次創建實例時恢復原型 if (mark_resume) { //var t = F.prototype.backUp_prototype; //var m = F.prototype; //還原原型 //extendDeep(F.prototype.backUp_prototype, F.prototype); extendDeep(F.backUp_prototype, F.prototype); //F.prototype.backUp_prototype = extendDeep(F.prototype); } else { mark_resume = true; } // 如果當前處於實例化類的階段,則調用Init原型函數 if (!initializing) { // console.log("F"); // // 如果父類存在,則實例對象的baseClass指向父類的原型 // // 這就提供了在實例對象中調用父類方法的途徑 // if (parentClass) { // this.baseClass = parentClass.prototype; // // console.log(this.baseClass); // } this.Init && this.Init.apply(this, arguments); } // this.Public = Public; // console.log("F"); // if (this.) // console.log(this._m); // delete this._m; // delete F.prototype._m; // delete F.prototype._a; // this._m = null; // this._a = null; // delete F.prototype._a; /*不能刪除私有成員和保護成員!否則類的成員就不能調用到私有和保護的成員了(因為已經刪除了)! 對象的創建算法參考http://www.cnblogs.com/TomXu/archive/2012/02/06/2330609.html //刪除私有成員和保護成員,這樣外界就不能訪問私有和保護成員了! for (name in this) { if (name.search(/(^_)|(^P_)/) !== -1) { delete F.prototype[name]; // this[name] = null; } } */ // console.log(count); // count++; //不使用MyClass.self!因為該屬性為靜態屬性,如果創建了該類后,又創建了類A,則MyClass.self會指向類A! // MyClass的靜態屬性self指向創建的類的實例,可以通過self來訪問實例的屬性和方法 // MyClass.self = this; // Private.C(); // for (name in Private) { // Private[name].call(this); // } } // Private.C = Private.C.call(null, Public); // Private.call(F, null); // Private.M = (function (pub) { // return function () { // Private.M.call(null, arguments); // } // }(Public)); // for (name in Private) { // Private[name] = function () { // // console.log("1111111"); // return function () { // // console.log("222222222"); // return Private[name].call(this, arguments); // } // }; // } // Private.C = function () { // return function () { // Private.C.call(F, arguments); // } // }; // 如果此類需要從其它類擴展 if (parentClass) { initializing = true; //F.prototype = new parentClass(); F.prototype = extendDeep(parentClass.prototype); F.prototype.constructor = F; // for (name in parentClass.prototype) { // if (parentClass.prototype.hasOwnProperty(name)) { // //私有成員不繼承 // if (!name.match(/^_/)) { // F.prototype[name] = parentClass.prototype[name]; // } // } // } // //刪除父類的私有成員,保留本類的私有成員 // for (name in parentClass.prototype) { // if (parentClass.prototype.hasOwnProperty(name)) { // //私有成員以“_”開頭,可能有多個“_”(多層繼承) // if (!name.match(/^_+/)) { // // delete parentClass.prototype[name]; // F.prototype[name] = parentClass.prototype[name]; // } // } // } // console.log(F.prototype.constructor); // 如果父類存在,則實例對象的baseClass指向父類的原型。 // 這就提供了在實例對象中調用父類方法的途徑。 //baseClass的方法是指向parentClass的,不是指向F(子類)的! //F.prototype.baseClass = parentClass.prototype; F.prototype[getNoRepeatStrInPrototype(parentClass.prototype, "baseClass")] = parentClass.prototype; initializing = false; } if (prop.Init) { // 如果此類繼承自父類parent並且父類原型中存在同名函數name if (parentClass && typeof prop.Init === "function" && typeof F.prototype.Init === "function") { F.prototype.Init = function (name) { return function () { this.base = function () { return parentClass.prototype[name].apply(parentClass.prototype, arguments); }; //指向子類,可以用於模版模式 this.baseToSubClass = parentClass.prototype[name]; //執行fn並返回執行的結果 //此處的arguments為F.prototype[name]方法傳入的形參。 return prop[name].apply(this, arguments); }; }("Init"); } else { F.prototype.Init = prop.Init; } } // Private.call(this); // if (parentClass && parentClass.prototype["JudgeDoubleHit"]) { // console.log(parentClass.toString()); if (parentClass) { for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { //如果不是抽象方法/保護方法/私有方法/接口成員,則加入到temp中。 //用於添加父類的密封方法(因為子類並沒有加入父類的密封方法)。 if (!name.match(/^Abstract_/) || !name.match(/^P_/) || !name.match(/^_/) || !name.match(/^Interface_/)) { temp[name] = parentClass.prototype[name]; } } } } // } // if (this.baseClass.Protected) { // if (this.baseClass.Protected.Sealed) { // for (k in this.baseClass.Protected.Sealed) { // temp[k] = this.baseClass.Protected.Sealed[k]; // } // } // } if (prop.Private) { //私有屬性/方法直接覆蓋 for (name in prop.Private) { if (prop.Private.hasOwnProperty(name)) { F.prototype[name] = prop.Private[name]; } } } // if (!prop.Public) { // throw new Error("Class must have public methods!"); // } // else { // } //保護成員 if (prop.Protected) { for (name in prop.Protected) { if (prop.Protected.hasOwnProperty(name)) { //檢查虛方法,虛方法放到Public或Protected中 if (name === "Virtual") { addVirtual(prop["Protected"][name], F, temp); continue; } //密封的方法(不允許子類重寫) if (name === "Sealed") { addSealed(prop["Protected"][name], F, temp); continue; } F.prototype[name] = prop.Protected[name]; //如果父類有保護抽象成員,此處檢查子類的保護成員是否實現了父類的保護抽象成員 temp[name] = prop.Protected[name]; } } } // //虛方法(不能為虛屬性) // if (prop.Virtual) { // for (name in prop.Virtual) { // if (prop.Virtual.hasOwnProperty(name)) { // if (typeof prop.Virtual[name] !== "function") { // throw new Error("Virtual attribute is not allowed!"); // } // else { // F.prototype[name] = prop.Virtual[name]; // temp[name] = prop.Virtual[name]; //加入temp // } // } // } // } if (prop.Abstract) { throw new Error("Only abstractClass can have abstract methods!"); } if (prop.Public) { // 覆蓋父類的同名公有方法 for (name in prop.Public) { // console.log("for in name = " + name); // //私有屬性/方法不加入到原型中 // if (name === "Private") { //// console.log("continue"); // continue; // } if (prop.Public.hasOwnProperty(name)) { //檢查虛方法,虛方法放到Public或Protected中 if (name === "Virtual") { addVirtual(prop["Public"][name], F, temp); continue; } //密封的方法(不允許子類重寫) if (name === "Sealed") { addSealed(prop["Public"][name], F, temp); continue; } // console.log("Public"); // console.log("name = " + name); // console.log("prop.Public[name] = " + prop.Public[name]); temp[name] = prop.Public[name]; //加入temp // 如果此類繼承自父類parent並且父類原型中存在同名函數name if (parentClass && typeof prop.Public[name] === "function" && typeof F.prototype[name] === "function") { // console.log("parent!"); F.prototype[name] = function (name) { return function () { /*此處如果寫成“this.base = parentClass.prototype[name];”,則在使用this.base()調用父類同名方法時, 父類同名方法的this指針是指向F的!(即指向子類,而不是指向父類!) 為什么???? 如: var Person = MyAbstract({ Init: function (name) { this.name = name; }, Public: { m: 1, getEmployeeID: function () { console.log(this.m); } } } }); var Employee = MyClass({ Init: function (name) { this.name = name; }, Public: { m: 100, getEmployeeID: function () { this.baseClass.getEmployeeID(); this.base(); } } }); var m = new Employee(); m.getEmployeeID(); //輸出:1 100 分析: this.baseClass.getEmployeeID()的this指向Person,而this.base()的this指向Employee! 解決方案: 用apply修正this.base()中的this,使其指向父類。 */ // if (!this.base) { //此處不用創建閉包了!因為外面已經創建了閉包,name已經被保存了! this.base = function () { //這個寫法也可以!為什么不用apply修正this也行??! //parentClass.prototype[name](); //此處的arguments為base方法傳入的形參 //注意!要加上“return”,這樣才能返回parentClass.prototype[name]的返回值 return parentClass.prototype[name].apply(parentClass.prototype, arguments); // this.baseClass. }; // } // if (!this.baseToSubClass) { //指向子類,可以用於模版模式 this.baseToSubClass = parentClass.prototype[name]; // } // this.base = function () { // // console.log(base_flag); // Private = { // }; //// base_flag = true; // return parent.prototype[name]; // }; // console.log("arg = " + arg); //執行fn並返回執行的結果 //此處的arguments為F.prototype[name]方法傳入的形參。 return prop.Public[name].apply(this, arguments); }; }(name); } else { // console.log(); //公有屬性 if (typeof (prop.Public[name]) !== "function") { F.prototype[name] = prop.Public[name]; } //公有方法 else { /* 如果不傳入Public[name],而直接在自執行函數中調用Public[name],如 F.prototype[name] = function () { return function () { prop.Public[name].apply(this, arguments); }; } (); 或者寫成: F.prototype[name] = function () { prop.Public[name].call(this, arguments); }; 這樣的話,在創建實例時調用方法時,都會執行MyClass中的最后一個函數!見下例 var Person = MyClass({ Init: function (name) { this.name = name; }, getName: function () { console.log("getName"); }, getEmployeeID: function ($private) { console.log("Person getEmployeeID"); } }); var m = new Person("name"); m.getName(); //第一種和第二種寫法此處會輸出:"Person getEmployeeID" 這樣執行的原因是: (引用自“深入理解JavaScript系列(16):閉包(Closures)”) 同一個父上下文中創建的閉包是共用一個[[Scope]]屬性的。也就是說, 某個閉包對其中[[Scope]]的變量做修改會影響到其他閉包對其變量的讀取。 這就是說:所有的內部函數都共享同一個父作用域。 也就是說,function里面的name都是共用父作用域中的name!所以此處F.prototype[name]被激活的時候, name都為最后一個值即"getEmployeeID"。 所以F原型上的方法都指向"getEmployeeID" 解決方案: 創建一個閉包來保存name的值。 */ F.prototype[name] = function (name) { return function () { return prop.Public[name].apply(this, arguments); //執行fn並返回執行的結果 }; }(name); } } } } } //檢查公有成員和虛函數是否實現了抽象方法/屬性 或 接口方法/屬性 check(parentClass, interface, temp); //靜態屬性/方法賦值 for (k in Static) { F[k] = Static[k]; } //備份原型 //F.prototype.backUp_prototype = extendDeep(F.prototype); F.backUp_prototype = extendDeep(F.prototype); return F; }; YYC.Pattern.namespace("Frame").MyInterface = MyInterface; YYC.Pattern.namespace("Frame").MyAbstract = MyAbstract; YYC.Pattern.namespace("Frame").MyClass = MyClass; }());
代碼分析
我並不打算對代碼詳細說明,因為該文的重點在於展示重構的過程。因此我介紹下原始版本實現OOP的核心內容,具體請參考代碼。
MyInterface為接口,MyAbstract為抽象類,MyClass為類。
創建一個接口,可以這樣寫:
var A = YYC.Frame.MyInterface("method1", "method2");
調用“YYC.Frame.MyInterface”時,會調用MyInterface函數,該函數會把參數解析,把方法和屬性加上前綴“Interface_”,加入到內部函數I.prototype中,然后返回內部函數I。
因此,A就具有了接口方法和接口屬性,但是我們不會直接使用接口(如創建A的實例,訪問接口方法),因為接口的方法和屬性(統稱為成員)並沒有實現,需要在繼承接口的類中實現。
然后創建一個抽象類:

var B = YYC.Frame.MyAbstract({ Protected: { //保護成員 Abstract: { //保護抽象成員 }, Virtual: { //保護虛方法 }, P_proA: true, //保護屬性 P_proM: function () { } //保護方法 }, Public: { //公有成員 Abstract: { //公有抽象成員 }, Virtual: { //公有虛方法 }, pubM: function () { }, //公有方法 pubA: 0 //公有屬性 }, Private: { //私有成員 _priA: "", //私有屬性 _priM: function () { } //私有方法 }, Abstract: { //公有抽象成員 }, Virtual: { //公有虛方法 } });
調用“YYC.Frame.MyAbstract”時,會調用MyAbstract函數,該函數會把參數解析,將公有成員、私有成員、保護成員都加入到內部函數A.prototype中(約定私有成員、保護成員的命名規則,約定使用框架者遵守訪問權限)。
抽象成員(Abstract:{}中的成員)先加上前綴“Abstract_”,然后加入到A.prototype中(子類根據前綴來區分判斷是否實現了父類的抽象成員)。
然后創建一個類,繼承接口A和抽象類B:

var C = YYC.Frame.MyClass({Interface: A, Class: B},{ Init: function () { //構造函數 }, Protected: { //保護成員 }, Public: { Virtual: { }, method1: function () { }, method2: function () { } }, Private: { //私有成員 _priA: "", //私有屬性 _priM: function () { } //私有方法 } });
調用“YYC.Frame.MyClass”時,會調用MyClass函數,該函數會把參數解析,將公有成員、私有成員、保護成員都加入到內部函數F.prototype中。
構造函數Init在F的構造函數function F(){}中調用,從而在創建C的實例時,會調用構造函數Init。
此處繼承了接口A和抽象類B,因此會先用深拷貝的方法來將A、B的成員加入到F.prototype中,然后判斷是否實現A的接口成員(根據“Interface_”前綴來判斷)、是否實現B的抽象成員(根據“Abstract_”前綴來判斷),如果沒有實現會拋出異常。
為什么要用深拷貝來實現繼承
//F.prototype = new parentClass(); F.prototype = extendDeep(parentClass.prototype);
此處繼承使用深拷貝來實現,原因是為了解決下面的問題:
- 若父類Parent的屬性為引用類型(數組或對象)a,有兩個子類Sub1、Sub2。如果子類Sub1的實例s1對a進行修改或者sub調用修改a的方法,則子類Sub2的實例的a為修改過后的a!
問題描述
var Parent = YYC.Frame.MyClass({ Private:{ _a: [] }, Public: { add: function () { this._a.push("a"); } } }); var Sub1 = YYC.Frame.MyClass(Parent, {}); var Sub2 = YYC.Frame.MyClass(Parent, {}); var t = new Sub1(); t.add(); console.log(t.a); //["a"] var k = new Sub2(); console.log(k.a); //照理說應該為[],但實際上卻是["a"]!
原因分析
上例中的“t.add();”修改的是實例t的_a屬性,實例t的_a屬性與Parent.prototype._a指向同一個數組。因此修改實例t的_a就相當於修改了Parent.prototype._a。
解決方案
修改類繼承方式,改為通過深拷貝的方式拷貝父類原型的成員來實現繼承:
F.prototype = extendDeep(parentClass.prototype);
這樣實例t的_a屬性和Parent.protype._a就指向不同的數組了。
為什么要重構
原始版本對其它的庫有依賴(如依賴於namespace.js),有太多沒用或錯誤的注釋,混在一起的職責,過於龐大的函數,函數名、屬性名不能很好地體現職責,多層嵌套的條件式等等。另外,最大的問題是沒有對應的測試套件。
開始重構
編寫測試
回到本次重構中來,要進行重構,首先需要堅固的測試作保證。我使用Jasmine作為Javascript的測試工具。建議大家先可以看下javascript單元測試,里面介紹了單元測試的工具,包括Jasmine。
先編寫幾個主要的測試,測試是否解決了我之前發現的幾個問題。

describe("oopFrame", function () { describe("測試Class", function () { describe("獲得公有成員", function () { it("如果父類不存在,能夠正確獲得公有方法", function () { var Class = YYC.Frame.MyClass({ Public: { a: function () { this.b = 1; return 0; } } }); var cla = new Class(); var result = cla.a(); expect(result).toEqual(0); expect(cla.b).toEqual(1); }); }); }); describe("集成測試", function () { it("測試解決“若父類的屬性為引用類型(數組或對象)a,則如果子類的實例s1對a進行修改或者sub調用修改a的方法,則第二次創建實例s2的a為修改過后的a!”的問題", function () { var Parent = YYC.Frame.MyAbstract({ Init: function () { console.log("Parent Init!"); }, Public: { a: [], } }); var Sub = YYC.Frame.MyClass(Parent, { Init: function () { }, Public: { } }); var t = new Sub(); t.a.push("a"); var m = new Sub(); expect(m.a).toEqual([]); }); it("測試解決“若父類Parent的屬性為引用類型(數組或對象)a,有兩個子類Sub1、Sub2。如果子類Sub1的實例s1對a進行修改或者sub調用修改a的方法,則子類Sub2的實例的a為修改過后的a!”的問題", function () { var Parent = YYC.Frame.MyAbstract({ Init: function () { console.log("Parent Init!"); }, Public: { a: [], add: function () { this.a.push("a"); } } }); var Sub1 = YYC.Frame.MyClass(Parent, { Init: function () { }, Public: { } }); var Sub2 = YYC.Frame.MyClass(Parent, { Init: function () { } }); var t = new Sub1(); t.a.push("a"); var k = new Sub2(); expect(k.a).toEqual([]); }); it("測試解決“若A1為抽象類,A2(抽象類)繼承於A1,B(類)繼承於A2,A1、A2、B都有同名方法a,A2和B在a方法中都通過this.baseClass調用父類同名方法。則如果B的實例b調用a方法,則A2、B的a方法中的this.baseClass均指向A2(照理說A2的this.baseClass應該指向A1)!”的問題", function () { var A1 = YYC.Frame.MyAbstract({ Public: { arr: [], a: function () { this.arr.push(1); } } }); var A2 = YYC.Frame.MyAbstract(A1, { Public: { a: function () { this.arr.push(2); this.baseClass.a.call(this, null); } } }); var B = YYC.Frame.MyClass(A2, { Public: { a: function () { this.arr.push(3); this._baseClass.a.call(this, null); return this.arr; } } }); var b = new B(); expect(b.a()).toEqual([3, 2, 1]); }); }); });
現在測試的覆蓋面還不全,有些代碼沒有測試到。不過我們已經構建了主要的測試,剩下的測試可以在后續的重構中逐漸加入。
測試頁面

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %> <!DOCTYPE HTML> <html> <head id="Head1" runat="server"> <title>Jasmine Spec Runner</title> <link rel="stylesheet" type="text/css" href="<%=Url.Content("~/Scripts/jasmine/lib/jasmine-1.3.1/jasmine.css") %>" /> <script src="<%=Url.Content("~/Scripts/jasmine/lib/jasmine-1.3.1/jasmine.js") %>"></script> <script src="<%=Url.Content("~/Scripts/jasmine/lib/jasmine-1.3.1/jasmine-html.js") %>"></script> <!-- include source files here... --> <script src="<%=Url.Content("~/Scripts/jquery-1.7.js")%>"></script> <script src="../../Scripts/myTool/pattern/createObject/namespace.js"></script> <script src="<%=Url.Content("~/Scripts/myTool/frame/YOOP.js")%>"></script> <!-- include spec files here... --> <script src="<%=Url.Content("~/Scripts/jasmine/spec/helper/specHelper.js")%>"></script> <script src="../../Scripts/jasmine/spec/frameSpec/YOOPSpec.js"></script> </head> <body> <script type="text/javascript"> (function () { var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; var htmlReporter = new jasmine.HtmlReporter(); jasmineEnv.addReporter(htmlReporter); jasmineEnv.specFilter = function (spec) { return htmlReporter.specFilter(spec); }; function execJasmine() { jasmineEnv.execute(); } var currentWindowOnload = window.onload; if (currentWindowOnload) { currentWindowOnload(); } execJasmine(); })(); </script> </body> </html>
在測試頁面中運行測試,通過全部測試。
解除對其它庫的依賴
現在該框架通過了測試,能夠正常工作。但是因為它采用命名空間模式,需要依賴於namespace.js庫。
我想解除這個依賴,因此直接在框架中定義YYC命名空間,並且考慮到該框架的通用性較高,因此將其命名空間YYC.Frame更改為YYC。
修改框架為:
window.YYC = window.YYC || {}; ... YYC.MyInterface = MyInterface; YYC.MyAbstract = MyAbstract; YYC.MyClass = MyClass;
測試頁面中去掉對namespace.js的引用。
修改類名
類名MyInterface、MyAbstract、MyClass太長了,而且個人氣息太濃,因此將類名改為Interface、AClass、Class。
重構this.base,this.baseToSubClass
原始版本:
A.prototype[name] = function (name) { return function () { //此處不用創建閉包了!因為外面已經創建了閉包,name已經被保存了! this.base = function () { //這個寫法也可以!為什么不用apply修正this也行??! //parentClass.prototype[name](); //此處的arguments為base方法傳入的形參 //注意!要加上“return”,這樣才能返回parentClass.prototype[name]的返回值 return abstractClass.prototype[name].apply(abstractClass.prototype, arguments); }; //指向子類,可以用於模版模式 this.baseToSubClass = abstractClass.prototype[name]; //執行fn並返回執行的結果 //此處的arguments為F.prototype[name]方法傳入的形參。 return prop.Public[name].apply(this, arguments); }; }(name);
原版中,子類使用this.base調用父類同名函數(函數中this指向父類同名函數),子類使用this.baseToSubClass調用父類同名函數(函數中this指向子類同名函數)。
考慮到一般都是使用this.baseToSubClass,因此將this.base改名為this.baseToParent,this.baseToSubClass改名為this.base,並不再使用this.baseToParent。
重構版本:
return function () { /* //此處不用創建閉包了!因為外面已經創建了閉包,name已經被保存了! this.baseToParrent = function () { //這個寫法也可以!為什么不用apply修正this也行??! //parentClass.prototype[name](); //此處的arguments為baseToParrent方法傳入的形參 //注意!要加上“return”,這樣才能返回parentClass.prototype[name]的返回值 return parentClass.prototype[name].apply(parentClass.prototype, arguments); }; */ //指向子類,可以用於模版模式 this.base = parentClass.prototype[name]; //執行fn並返回執行的結果 //此處的arguments為F.prototype[name]方法傳入的形參。 return prop[name].apply(this, arguments); };
重構結構
分析代碼結構,發現AClass與Class聯系的比較緊密,而Interface則可以單獨為一塊,因此對代碼進行分塊:
(function(){ //A結構 (function(){ //A1結構 function Interface(){ }; YYC.Interface = Interface; }()); (function(){ //A2結構 function AClass(){ }; function Class(){ }; YYC.AClass = AClass; YYC.Class = Class; }()); }());
提取工具函數
在工具函數的名字中加入前綴“_”,表示為私有函數。
將Interface、AClass、Class共用的工具函數提出來,放到A中。然后將AClass、Class共用的工具函數提出來放到A2中:

(function(){ //A結構 window.YYC = window.YYC || {}; /************************************************** String對象擴展 *********************************************************** 擴展方法: contain containIgnoreCase trim */ if (!String.prototype.contain) { String.prototype.contain = function (str) { var reg = new RegExp(str); if (this.match(reg)) { //用this指針指代本體 return true; } else { return false; } } } /*****************************************************************************************************************************/ ////獲得函數的參數數組 //function argumentNames(fn) { // var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g, '').split(','); // return names.length == 1 && !names[0] ? [] : names; //}; //獲得函數名 function _getFunctionName(fn) { var name = ""; if (!fn) { return null; } name = fn.toString().match(/^.*function\s*([^\(]*)/); return name === null ? name : name[1]; }; //判斷是否為數組 function _isArray(val) { return Object.prototype.toString.call(val) === "[object Array]"; }; (function(){ //A1結構 function Interface(){ }; }()); (function(){ //A2結構 /* 深拷貝 */ function _extendDeep(parent, child) { var i = null, len = 0, toStr = Object.prototype.toString, sArr = "[object Array]", sOb = "[object Object]", type = "", _child = null; //數組的話,不獲得Array原型上的成員。 if (toStr.call(parent) === sArr) { _child = child || []; for (i = 0, len = parent.length; i < len; i++) { type = toStr.call(parent[i]); if (type === sArr || type === sOb) { //如果為數組或object對象 _child[i] = type === sArr ? [] : {}; _extendDeep(parent[i], _child[i]); } else { _child[i] = parent[i]; } } } //對象的話,要獲得原型鏈上的成員。因為考慮以下情景: //類A繼承於類B,現在想要拷貝類A的實例a的成員(包括從類B繼承來的成員),那么就需要獲得原型鏈上的成員。 else if (toStr.call(parent) === sOb) { _child = child || {}; for (i in parent) { //if (parent.hasOwnProperty && parent.hasOwnProperty(i)) { // if (typeof parent[i] === 'object') { //null === 'object'也為true! type = toStr.call(parent[i]); if (type === sArr || type === sOb) { //如果為數組或object對象 _child[i] = type === sArr ? [] : {}; _extendDeep(parent[i], _child[i]); } else { _child[i] = parent[i]; } } //} } else { _child = parent; } return _child; }; //獲得在原型prototype中不存在同名的str。 //如果有同名,則加上前綴"_" function _getNoRepeatStrInPrototype(prototype, str) { var new_str = ""; if (!prototype[str]) { return str; } new_str = "_" + str; return _getNoRepeatStrInPrototype(prototype, new_str); } function AClass(){ }; function Class(){ }; YYC.AClass = AClass; YYC.Class = Class; }()); }());
重構_check函數
注意到_check函數太大,因此需要根據職責來提取出小函數。
_check有兩個職責:
- 檢查是否實現了父類的抽象方法/屬性。
- 檢查是否實現了接口方法/屬性。
重構前:

function _check(parentClass, interface, children) { var name = ""; if (parentClass) { //檢查是否實現了抽象方法/屬性 for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { if (name === "constructor") { continue; } if (name.contain("Abstract_")) { //抽象方法 if (typeof parentClass.prototype[name] === "function") { if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] !== "function") { throw new Error("Abstract method '" + name + "' must be overwrited!"); } } //抽象屬性 else { if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] === "function") { throw new Error("Abstract attribute '" + name + "' must be overwrited!"); } } } } } } if (!interface) { return; } //檢查是否實現了接口方法/屬性 for (name in interface.prototype) { if (name === "constructor") { continue; } //接口方法 if (typeof interface.prototype[name] === "function") { if (children[name.slice(10)] === undefined || typeof children[name.slice(10)] !== "function") { throw new Error("Interface method '" + name + "' must be overwrited!"); } } //接口屬性 else { if (children[name.slice(10)] === undefined || typeof children[name.slice(10)] === "function") { throw new Error("Interface attribute '" + name + "' must be overwrited!"); } } } };
重構后:

//檢查子類的公有方法+虛方法+抽象方法是否包含父類的抽象方法/屬性 或 接口方法/屬性。 //不用hasOwnProperty判斷!否則就檢查不到是否包含了父類的抽象方法/屬性 或 接口方法/屬性。 function _check(parentClass, interface, children) { if (parentClass) { _checkAbstract(parentClass, children); } else if (interface) { _checkInterface(interface, children); } }; function _checkAbstract(parentClass, children) { var name = ""; for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { if (name === "constructor") { continue; } if (name.contain("Abstract_")) { //抽象方法 if (typeof parentClass.prototype[name] === "function") { if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] !== "function") { throw new Error("Abstract method '" + name + "' must be overwrited!"); } } //抽象屬性 else { if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] === "function") { throw new Error("Abstract attribute '" + name + "' must be overwrited!"); } } } } } }; function _checkInterface(interface, children) { var name = ""; for (name in interface.prototype) { if (name === "constructor") { continue; } //接口方法 if (typeof interface.prototype[name] === "function") { if (children[name.slice(10)] === undefined || typeof children[name.slice(10)] !== "function") { throw new Error("Interface method '" + name + "' must be overwrited!"); } } //接口屬性 else { if (children[name.slice(10)] === undefined || typeof children[name.slice(10)] === "function") { throw new Error("Interface attribute '" + name + "' must be overwrited!"); } } } };
_checkAbstract、_checkInterface中的“if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] !== "function") {”等條件語句很難理解,因此將其封裝成函數:

function _checkAbstract(parentClass, children) { var name = ""; for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { if (name === "constructor") { continue; } if (name.contain("Abstract_")) { //抽象方法 if (typeof parentClass.prototype[name] === "function") { if (_noMethodForAbstract(children, name) && _noMethodForAbstract(parentClass.prototype, name)) { throw new Error("Abstract method '" + name + "' must be overwrited!"); } } //抽象屬性 else { if (_noAttritubeForAbstract(children, name) && _noAttritubeForAbstract(parentClass.prototype, name)) { throw new Error("Abstract attribute '" + name + "' must be overwrited!"); } } } } } }; function _checkInterface(interface, children) { var name = ""; for (name in interface.prototype) { if (name === "constructor") { continue; } //接口方法 if (typeof interface.prototype[name] === "function") { if (_noMethodForInterface(children, name) && _noMethodForInterface(parentClass.prototype, name)) { throw new Error("Interface method '" + name + "' must be overwrited!"); } } //接口屬性 else { if (_noAttritubeForInterface(children, name) && _noAttritubeForInterface(parentClass.prototype, name)) { throw new Error("Interface attribute '" + name + "' must be overwrited!"); } } } }; function _noMethodForAbstract(_class, name) { return _class[name.slice(9)] === undefined || typeof _class[name.slice(9)] !== "function"; }; function _noAttritubeForAbstract(_class, name) { return _class[name.slice(9)] === undefined || typeof _class[name.slice(9)] === "function"; }; function _noMethodForInterface(_class, name) { return _class[name.slice(10)] === undefined || typeof _class[name.slice(10)] !== "function"; }; function _noAttritubeForInterface(_class, name) { return _class[name.slice(10)] === undefined || typeof _class[name.slice(10)] === "function"; };
重構Interface
現在讓我們看下function Interface(){}中的代碼:

//創建接口 //接口可以繼承接口 function Interface(_parent, _method, _attribute) { var i = 0, args = null; var parent = null, method = null, attribute = null; if (typeof _parent === "function") { if (_getFunctionName(_parent) !== "I") { throw new Error("Interface must inherit interface!"); } else { parent = _parent; //形如“MyInterface(Parent, "A", "B", "GetName");” if (_method && !_isArray(_method)) { method = Array.prototype.slice.call(arguments, 1); attribute = null; } //形如“MyInterface(Parent, ["A", "B", "GetName"], ["a", "c"]);” else { method = _method; attribute = _attribute; } } // console.log(parent.toString()); } else { parent = null; //形如“MyInterface("A", "B", "GetName");” if (_method && !_isArray(_method)) { method = arguments attribute = null; } //形如“MyInterface(["A", "B", "GetName"], ["a", "c"]);” else { method = arguments[0]; attribute = arguments[1]; } } function I() { } // 如果此接口需要從其它接口擴展 if (parent) { I.prototype = new parent(); I.prototype.constructor = I; } // console.log("method = " + method); // console.log("attribute = " + attribute); // //形如“MyInterface(["A", "B", "GetName"], ["a", "c"]);” // if (isArray(method)) { //方法 for (i = 0; i < method.length; i++) { //加上前綴“Interface_” I.prototype["Interface_" + method[i]] = function () { throw new Error("This method must be overwrited!"); }; } //屬性 if (attribute) { if (!isArray(attribute)) { throw new Error("Attribute must be array!"); } else { for (i = 0; i < attribute.length; i++) { //加上前綴“Interface_” I.prototype["Interface_" + attribute[i]] = 0; } } } // } // //形如“MyInterface("A", "B", "GetName");” // else { // args = Array.prototype.slice.call(arguments, 1); // //方法 // for (i = 0; i < args.length; i++) { // I.prototype[args[i]] = function () { // throw new Error("This method must be overwrited!"); // }; // } // } return I; };
該函數包含了太多的職責,應該把每一個職責提取為一個內部函數,然后再在Interface中調用這些內部函數:

//創建接口 //接口可以繼承接口 function Interface(_parent, _method, _attribute) { var i = 0, args = null; var parent = null, method = null, attribute = null; function _getByParent() { if (typeof _parent === "function") { if (_getFunctionName(_parent) !== "I") { throw new Error("Interface must inherit interface!"); } else { parent = _parent; //形如“Interface(Parent, "A", "B", "GetName");” if (_method && !_isArray(_method)) { method = Array.prototype.slice.call(arguments, 1); attribute = null; } //形如“Interface(Parent, ["A", "B", "GetName"], ["a", "c"]);” else { method = _method; attribute = _attribute; } } } else { parent = null; //形如“Interface("A", "B", "GetName");” if (_method && !_isArray(_method)) { method = arguments attribute = null; } //形如“Interface(["A", "B", "GetName"], ["a", "c"]);” else { method = arguments[0]; attribute = arguments[1]; } } }; function _inherit() { I.prototype = new parent(); I.prototype.constructor = I; }; function _addMethod() { for (i = 0; i < method.length; i++) { //加上前綴“Interface_” I.prototype["Interface_" + method[i]] = function () { throw new Error("This method must be overwrited!"); }; } }; function _addAttribute() { if (attribute) { if (!_isArray(attribute)) { throw new Error("Attribute must be array!"); } else { for (i = 0; i < attribute.length; i++) { //加上前綴“Interface_” I.prototype["Interface_" + attribute[i]] = 0; } } } }; function I() { } _getByParent(); // 如果此接口需要從其它接口擴展 if (parent) { _inherit(); } //方法 _addMethod(); //屬性 _addAttribute(); return I; };
重構AClass、Class
AClass、Class中有一些重復的代碼,將這些重復的代碼提取為函數,放到結構A2中,供AClass、Class調用:

(function(){ //A2結構 //檢查抽象成員 function _addAbstract(abstract, currentClass, temp) { var name = ""; for (name in abstract) { if (abstract.hasOwnProperty(name)) { //抽象方法前面加"Abstract_"前綴 currentClass.prototype["Abstract_" + name] = abstract[name]; temp[name] = abstract[name]; //加入temp } } }; //檢查虛方法(不能為虛屬性) function _addVirtual(virtual, currentClass, temp) { var name = ""; for (name in virtual) { if (virtual.hasOwnProperty(name)) { if (typeof virtual[name] !== "function") { throw new Error("Virtual attribute is not allowed!"); } else { currentClass.prototype[name] = virtual[name]; temp[name] = virtual[name]; //加入temp } } } }; //加入密封方法。 //沒有實現檢查子類是否重寫了父類的密封方法,只是定義了一個規范。 function _addSealed(sealed, currentClass, temp) { var name = ""; for (name in sealed) { if (sealed.hasOwnProperty(name)) { currentClass.prototype[name] = sealed[name]; temp[name] = sealed[name]; //加入temp } } }; function _addStatic(_class, prop) { var Static = null; var k = null; Static = prop.Static ? prop.Static : null; //靜態屬性/方法賦值 for (k in Static) { _class[k] = Static[k]; } }; function _inherit(_class, parentClass) { _class.prototype = _extendDeep(parentClass.prototype); _class.prototype.constructor = _class; // 如果父類存在,則實例對象的baseClass指向父類的原型。 // 這就提供了在實例對象中調用父類方法的途徑。 //baseClass的方法是指向parentClass的,不是指向F(子類)的! _class.prototype[_getNoRepeatStrInPrototype(parentClass.prototype, "baseClass")] = parentClass.prototype; }; function _addInit(_class, parentClass, prop) { if (prop.Init) { // 如果此類繼承自父類parent並且父類原型中存在同名函數name if (parentClass && typeof prop.Init === "function" && typeof _class.prototype.Init === "function") { //if (parentClass) { _class.prototype.Init = function (name) { return function () { /* //此處不用創建閉包了!因為外面已經創建了閉包,name已經被保存了! this.baseToParrent = function () { //這個寫法也可以!為什么不用apply修正this也行??! //parentClass.prototype[name](); //此處的arguments為baseToParrent方法傳入的形參 //注意!要加上“return”,這樣才能返回parentClass.prototype[name]的返回值 return parentClass.prototype[name].apply(parentClass.prototype, arguments); }; */ //指向子類,可以用於模版模式 this.base = parentClass.prototype[name]; //執行fn並返回執行的結果 //此處的arguments為F.prototype[name]方法傳入的形參。 return prop[name].apply(this, arguments); }; }("Init"); } else { _class.prototype.Init = prop.Init; } } }; function _addPrivate(_class, private) { if (private) { //私有屬性/方法直接覆蓋 for (name in private) { if (private.hasOwnProperty(name)) { _class.prototype[name] = private[name]; } } } }; //檢查抽象類的公有方法+虛方法+抽象方法是否包含父類的抽象方法/屬性 或 接口方法/屬性。 //不用hasOwnProperty判斷!否則就檢查不到是否包含了父類的抽象方法/屬性 或 接口方法/屬性。 function _check(parentClass, interface, children) { if (parentClass) { _checkAbstract(parentClass, children); } else if (interface) { _checkInterface(interface, children); } }; function _checkAbstract(parentClass, children) { var name = ""; for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { if (name === "constructor") { continue; } if (name.contain("Abstract_")) { //抽象方法 if (typeof parentClass.prototype[name] === "function") { if (_noMethodForAbstract(children, name) && _noMethodForAbstract(parentClass.prototype, name)) { throw new Error("Abstract method '" + name + "' must be overwrited!"); } } //抽象屬性 else { if (_noAttritubeForAbstract(children, name) && _noAttritubeForAbstract(parentClass.prototype, name)) { throw new Error("Abstract attribute '" + name + "' must be overwrited!"); } } } } } }; function _checkInterface(interface, children) { var name = ""; for (name in interface.prototype) { if (name === "constructor") { continue; } //接口方法 if (typeof interface.prototype[name] === "function") { if (_noMethodForInterface(children, name) && _noMethodForInterface(parentClass.prototype, name)) { throw new Error("Interface method '" + name + "' must be overwrited!"); } } //接口屬性 else { if (_noAttritubeForInterface(children, name) && _noAttritubeForInterface(parentClass.prototype, name)) { throw new Error("Interface attribute '" + name + "' must be overwrited!"); } } } }; function _noMethodForAbstract(_class, name) { return _class[name.slice(9)] === undefined || typeof _class[name.slice(9)] !== "function"; }; function _noAttritubeForAbstract(_class, name) { return _class[name.slice(9)] === undefined || typeof _class[name.slice(9)] === "function"; }; function _noMethodForInterface(_class, name) { return _class[name.slice(10)] === undefined || typeof _class[name.slice(10)] !== "function"; }; function _noAttritubeForInterface(_class, name) { return _class[name.slice(10)] === undefined || typeof _class[name.slice(10)] === "function"; }; function AClass(){ }; function Class(){ }; YYC.AClass = AClass; YYC.Class = Class; }());
將AClass中的每一個職責提取為一個內部函數,然后再在AClass中調用這些內部函數:

//創建抽象類 //抽象類能夠繼承接口、抽象類以及實體類,但此處約定抽象類只能繼承接口和抽象類,不能繼承實體類! //(這樣方便判斷抽象類是否包含全部的父類(接口/抽象類)成員) function AClass(_parent, _prop) { var name = null, temp = {}; var parentClass = null, interface = null, prop = null; ////原型恢復標志,用於防止第一次創建實例時恢復原型 //var mark_resume = false; function _getByParent() { //if (arguments.length === 1) { if (_prop === undefined) { prop = _parent; parentClass = null; interface = null; } else if (typeof _parent === "object") { if (!_parent.Class && !_parent.Interface) { throw new Error("Please add AbstractClass or Interface!"); } if (_getFunctionName(_parent.Class) === "F" || _getFunctionName(_parent.Interface) === "F") { throw new Error("AbstractClass here can't inherit parentClass which is created by Class function!"); } parentClass = _parent.Class; interface = _parent.Interface; prop = _prop; } //_parent直接為xx,就表示父類為抽象類 else if (typeof _parent === "function") { if (_getFunctionName(_parent) === "F") { throw new Error("AbstractClass here can't inherit parentClass which is created by Class function!"); } parentClass = _parent; interface = null; prop = _prop; } else { throw new Error("arguments is not allowed!"); } }; function _prepareAndAddPublic() { if (prop.Public) { for (name in prop.Public) { if (prop.Public.hasOwnProperty(name)) { if (_prepareCheck("Public") === "continue") { continue; } _addPublic(); } } } }; function _addPublic() { if (parentClass && typeof prop.Public[name] === "function" && typeof A.prototype[name] === "function") { A.prototype[name] = function (name) { return function () { /* //此處不用創建閉包了!因為外面已經創建了閉包,name已經被保存了! this.baseToParrent = function () { //這個寫法也可以!為什么不用apply修正this也行??! //parentClass.prototype[name](); //此處的arguments為baseToParrent方法傳入的形參 //注意!要加上“return”,這樣才能返回parentClass.prototype[name]的返回值 return parentClass.prototype[name].apply(parentClass.prototype, arguments); }; */ //指向子類,可以用於模版模式 this.base = parentClass.prototype[name]; //執行fn並返回執行的結果 //此處的arguments為F.prototype[name]方法傳入的形參。 return prop.Public[name].apply(this, arguments); }; }(name); } else { A.prototype[name] = prop.Public[name]; } } function _prepareAndAddProtected() { if (prop.Protected) { for (name in prop.Protected) { if (prop.Protected.hasOwnProperty(name)) { if (_prepareCheck("Protected") === "continue") { continue; } A.prototype[name] = prop.Protected[name]; } } } }; function _prepareCheck(where) { //檢查抽象成員,抽象成員放到Public或Protected中 if (name === "Abstract") { _addAbstract(prop[where][name], A, temp); return "continue"; } //檢查虛方法,虛方法放到Public或Protected中 if (name === "Virtual") { _addVirtual(prop[where][name], A, temp); return "continue"; } //密封的方法(不允許子類重寫) if (name === "Sealed") { _addSealed(prop[where][name], A, temp); return "continue"; } temp[name] = prop[where][name]; //用於檢查是否包含父類的抽象方法/屬性 或 接口方法/屬性 return null; }; // 本次調用所創建的類(構造函數) function A() { } //取出父類、接口 _getByParent(); // 如果此接口需要從其它接口擴展 if (parentClass) { _inherit(A, parentClass); } //加入構造函數 //抽象類本身因為不能實例化,所以不調用構造函數。 //抽象類中的構造函數供子類構造函數中調用。 _addInit(A, parentClass, prop); _addPrivate(A, prop.Private); _prepareAndAddPublic(); //保護成員 _prepareAndAddProtected(); //放到外面的抽象成員,默認為公有抽象成員 _addAbstract(prop.Abstract, A, temp); _addStatic(A, prop); //檢查抽象類的公有方法+虛方法+抽象方法是否包含父類的接口方法/屬性 _check(null, interface, temp); return A; };
同理Class重構為:

//創建普通類 //父類_parent可以為{Class: xx, Interface: xx},或者直接為xx類 function Class(_parent, _prop) { //當前是否處於創建類的階段。 var initializing = false; var name = null; var parentClass = null, interface = null, prop = null, temp = {}; //原型恢復標志,用於防止第一次創建實例時恢復原型 var mark_resume = false; function _getByParent() { if (_prop === undefined) { prop = _parent; parentClass = null; interface = null; } //{Class: xx, Interface: xx} else if (typeof _parent === "object") { if (!_parent.Class && !_parent.Interface) { throw new Error("Please add Class or Interface!"); } parentClass = _parent.Class; interface = _parent.Interface; prop = _prop; } //直接為xx類 else if (typeof _parent === "function") { parentClass = _parent; interface = null; prop = _prop; } else { throw new Error("arguments is not allowed!"); } }; function _addParentSealed() { for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { //如果不是抽象方法/保護方法/私有方法/接口成員,則加入到temp中。 //用於添加父類的密封方法(因為子類並沒有加入父類的密封方法)。 if (!name.match(/^Abstract_/) || !name.match(/^P_/) || !name.match(/^_/) || !name.match(/^Interface_/)) { temp[name] = parentClass.prototype[name]; } } } }; function _prepareAndAddPublic() { if (prop.Public) { for (name in prop.Public) { if (prop.Public.hasOwnProperty(name)) { if (_prepareCheck("Public") === "continue") { continue; } _addPublic(); } } } }; function _addPublic() { // 如果此類繼承自父類parent並且父類原型中存在同名函數name if (parentClass && typeof prop.Public[name] === "function" && typeof F.prototype[name] === "function") { F.prototype[name] = function (name) { return function () { //指向子類,可以用於模版模式 this.base = parentClass.prototype[name]; //執行fn並返回執行的結果 //此處的arguments為F.prototype[name]方法傳入的形參。 return prop.Public[name].apply(this, arguments); }; }(name); } else { F.prototype[name] = prop.Public[name]; } } function _prepareAndAddProtected() { if (prop.Protected) { for (name in prop.Protected) { if (prop.Protected.hasOwnProperty(name)) { if (_prepareCheck("Protected") === "continue") { continue; } F.prototype[name] = prop.Protected[name]; } } } }; function _prepareCheck(where) { //檢查虛方法,虛方法放到Public或Protected中 if (name === "Virtual") { _addVirtual(prop[where][name], A, temp); return "continue"; } //密封的方法(不允許子類重寫) if (name === "Sealed") { _addSealed(prop[where][name], A, temp); return "continue"; } temp[name] = prop[where][name]; //用於檢查是否包含父類的抽象方法/屬性 或 接口方法/屬性 return null; }; _getByParent(); // 本次調用所創建的類(構造函數) function F() { //防止第一次創建實例時恢復原型 if (mark_resume) { //還原原型 _extendDeep(F.backUp_prototype, F.prototype); } else { mark_resume = true; } // 如果當前處於實例化類的階段,則調用Init原型函數 if (!initializing) { this.Init && this.Init.apply(this, arguments); } /*不能刪除私有成員和保護成員!否則類的成員就不能調用到私有和保護的成員了(因為已經刪除了)! 對象的創建算法參考http://www.cnblogs.com/TomXu/archive/2012/02/06/2330609.html //刪除私有成員和保護成員,這樣外界就不能訪問私有和保護成員了! for (name in this) { if (name.search(/(^_)|(^P_)/) !== -1) { delete F.prototype[name]; // this[name] = null; } } */ } // 如果此類需要從其它類擴展 if (parentClass) { initializing = true; _inherit(F, parentClass); initializing = false; } _addInit(F, parentClass, prop); if (parentClass) { _addParentSealed(); } _addPrivate(F, prop.Private); //保護成員 _prepareAndAddProtected(); if (prop.Abstract) { throw new Error("Only abstractClass can have abstract methods!"); } _prepareAndAddPublic(); //檢查公有成員和虛函數是否實現了抽象方法/屬性 或 接口方法/屬性 _check(parentClass, interface, temp); _addStatic(F, prop); //備份原型 F.backUp_prototype = _extendDeep(F.prototype); return F; };
重命名temp
AClass和Class中的局部屬性temp的職責是存儲該類成員的名稱,從而用於檢查該類成員是否實現了接口或者父類的抽象成員。
因此,將temp改名為children,這樣能反映職責。