javascript採用原型繼承的方式繼承一個類(javascript沒有類這個概念,暫時這麼稱呼吧),但一些使用過Java的程序員可能習慣使用經典的類繼承,但javascript原生並不支持這種方式,所以須要手動實現。這裏經過定義一個定義類(defineClass)的函數實現,經測試越用越順手。因爲javascript沒有訪問修飾符,所以若是須要使用到private成員,請使用閉包。javascript
1 /* 簡單的對象擴充方法 2 */ 3 merge:function (target, origin) { 4 for (var attribute in origin) { 5 if (origin.hasOwnProperty(attribute)) { 6 target[attribute] = origin[attribute]; 7 } 8 } 9 }, 10 defineClass:function defineClass(constructor, parent, properties, statics, isSingleton) { 11 12 /* 若是爲單例模式,保存實例,並在之後的調用中返回此實例 13 */ 14 if (isSingleton) { 15 var oldConstructor = constructor, 16 instance; 17 constructor = function () { 18 if (instance) return instance; 19 oldConstructor.apply(this, arguments); 20 instance = this; 21 } 22 } 23 24 /* 設置原型屬性,這意味着傳入的構造函數的原型屬性將被覆蓋 25 * 重要:parent內部須要檢測參數,下面將會講到 26 */ 27 constructor.prototype = parent ? new parent() : {}; 28 29 /* 將自有屬性複製到原型中 30 * 將靜態屬性複製到構造函數中,這意味着將不會繼承parent的靜態屬性 31 */ 32 Lang.merge(constructor.prototype, properties); 33 Lang.merge(constructor, statics); 34 35 /* 將構造函數更改成當前構造函數 36 * 將parent的引用保留 37 */ 38 constructor.prototype.constructor = constructor; 39 constructor.prototype.parent = parent; 40 constructor.parent = parent; 41 42 /* 借用父類函數 43 */ 44 constructor.borrow = function(methodName, context, args){ 45 var oldParent; 46 47 if(typeof methodName === "object") { 48 args = context; 49 context = methodName; 50 } 51 52 oldParent = context.parent; 53 context.parent = parent; 54 55 if(typeof methodName === "string") { 56 constructor.prototype[methodName].apply(context, args || []); 57 } else { 58 constructor.apply(context, args || []); 59 } 60 61 context.parent = oldParent; 62 }; 63 return constructor; 64 }
使用時,父類如此定義java
1 var Body = Lang.defineClass(function(x, y, shapes, sprites, detection){ 2 /* 因爲類定義函數會建立父類實例做爲子類的原型,所以必須檢測參數 3 * 若是沒有參數,至關於調用父類的默認構造函數 4 */ 5 if(arguments.length) { 6 /* 使用父類方法 7 */ 8 this.parent.borrow(this, arguments); 9 10 /* 非基本類型請放到構造函數中而不是原型中 11 */ 12 this._tasks = []; 13 } 14 }, Object, { 15 _distance: 0, 16 setX: function(x){}, 17 setY: function(y){} 18 }, { 19 type: 'Base' 20 });
繼承Body程序員
1 var Ninja = Lang.defineClass(function(){ 2 this.parent.borrow(this, arguments); 3 }, Body, { 4 _status: 1, 5 setStatus: function(x){ 6 /* 使用父類方法 7 */ 8 this.parent.borrow('setStatus', this, arguments); 9 } 10 }, {});
注意,不能用此方法繼承如Array等內置對象,若是你想經過定義一個類擴展Array的功能,那麼在調用Array的某些方法時會出現問題,好比concat返回的數組直接包含兩個對象,而不是包含兩個對象中的元素。緣由是雖然子類的原型鏈包含Array.prototype,但畢竟不是由Array直接構造,在調用某些方法時可能不會按照原始的方式執行。數組