JavaScript模拟Java类继承

javascript采用原型继承的方式继承一个类(javascript没有类这个概念,暂时这么称呼吧),但一些使用过Java的程序员可能习惯使用经典的类继承,但javascript原生并不支持这种方式,因此需要手动实现。这里通过定义一个定义类(defineClass)的函数实现,经测试越用越顺手。由于javascript没有访问修饰符,因此如果需要使用到private成员,请使用闭包。

 

 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 }

 

使用时,父类如此定义

 

 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直接构造,在调用某些方法时可能不会按照原始的方式执行。

你可能感兴趣的:(JavaScript)