JavaScript模擬Java類繼承

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直接構造,在調用某些方法時可能不會按照原始的方式執行。數組

相關文章
相關標籤/搜索