论坛首页 Web前端技术论坛

Javascript实现类继承特性的另一种方法

浏览 2996 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-04-02   最后修改:2009-05-20

 

  转自:http://www.bgscript.com - 背光脚本

 

类的继承特性可以有效的重用源代码,提高开发效率.

在WEB开发中,无论是富客端页面还是一般的网页, 可重用的地方都很多, 通过Javascript以对象方式来组织这些重用代码结构清晰,易于维护与扩展,复用类可以减

 

少JS文本体积,加快下载速度.

但Javascript语言本身并不提供面向对象(封装,多态,继承)的原生支持,具体还得手动设计.

设计方式很多,但原理都是复制对象属性产生新类,通过apply或call方法调用父类方法.

例如,

 

> Ext库:

 

在类声明时为

Ext.CurrentClass = Ext.extend(Ext.SuperClass, { ... });

 

在设计类中调用父类初始化方法为

 

Ext.CurrentClass.superclass.initComponent.call(this);

 

> jQuery:

 

在类声明时为

$.widget("ui.currentclass",{ ... });

 

在设计类中调用父类初始化方法更为显式

$.widget.prototype. initComponent.apply(this, arguments);

 

以上实现方式或多或少有些冗长, 根据笔者经验,有一种更好的方法, 

 

例如 :

 

  var MyClass = CC.create(SuperClass, function(superclass){
      
      return {
        initComponent : function(options) {
           superclass.initComponent.call(this, options);
           // other codes... 
       }
      };
  });

 

 

 

 

这种方式有它的优点:

  构建够直观,简洁. 它是将整个类包装在一起的,浑然一体,像Java类一样, 父子关系明了.

  父类访问方便, 快速. 该方法把父类作为参数superclass传进来构建,子类方法通过JS闭包特性直接引用, 快速高效.子类方法中完全不必知道父类是谁.

  类可以共享一些其它类没有的东西, 如变量, 方法等, 这有点像Java中类的static成员.

 

实现方法如下

做些前奏准备,写一个99.99%的JS库都会有的方法

 

//
//对象属性复制方法,很多库都有实现,如PrototypeJS里面的extend和Ext里面的Ext.apply
//
function extend(des, src) {
  if (!des)
    des = {};
    
  if (src) {
    for (var i in src) {
    	des[i] = src[i];
    }
  }
  
  return des;
}

 

再定个对象,名称任意,主要是挂个方法而已,这里简单起见写为CC.

 

var CC = {};

 

以下是实现主要函数,主要是组装返回要的对象.

 

//
//create 用于创建类
//
CC.create = function(superclass, constructor){
   var clazz = (function() {
      this.initialize.apply(this, arguments);
   });
	
	//如果无参数,直接返回类.
	if(arguments.length == 0)
    return clazz;
  
  //如果无父类,此时constructor应该为一个纯对象,直接复制属性返回.
  if(!superclass){
  	extend(clazz.prototype, constructor);
  	return clazz;
  }
  
  var absObj = clazz.prototype, 
      sprPropty = superclass.prototype;

  if(sprPropty){
			//用于访问父类方法
      clazz.superclass = sprPropty;
      extend(absObj, sprPropty);
      
      //调用属性构造函数创建属性,这个是实现关键.
      extend(absObj, constructor(sprPropty));

      // 子类实例直接通过obj.superclass访问父类属性,
      // 如果不想造成过多引用,也可把这句注释掉,因为多数时候是没必要的.
      absObj.superclass = sprPropty;
      //
      clazz.constructor = constructor;
  }
  
  return clazz;
}

 

OK, 大功告成,写个类承继例子测试一下.

 

//
//创建一个动物类
//
var Animal = CC.create(null, {

	//属性
	footprint  : '- - - - - - =',
		
	//类初始化方法,必须的,当用 new 生成一个类时该方法自动被调用,参见上定义.
	initialize : function(options){
		extend(this, options);
		alert('Animal initialize method is called.');
	},
	
	eat : function(){
		alert('Animal eat method is called.');
	},
	
	move : function(){
		alert('I am moving like this '+  this.footprint +' .');
	}
});

//
//创建一个Duke类
//
var Duke = CC.create(Animal, function(superclass){
	
	//在这可以定义一些类全局静态数据,该类每个实例都共享这些数据.
	//计算实例个类,包括派生类实例.
	var static_instance_counter = 0;
	
	function classUtilityFuncHere(){  }
	
	//返回类具体属性.
	return {
		//重写初始化方法
		//@override
		initialize : function(options) {
			alert('Initializing Duke class..');
			
			//调用父类初始化,这种方法比一般其它库的要简洁点吧,可以不管父类是什么.
			superclass.initialize.call(this, options);
			
			//做一些子类喜欢做的事.
			alert('Duke initialize method is called.');
			
			//读取或修改类静态属性
			static_instance_counter++;
		},
		
		//重写move方法,增加Duke自己的移动方式.
		move : function(){
			this.footprint = this.footprint + 'zzzzzzzz';
			
			superclass.move.call(this);
		},
		
		
		//重写eat方法,注意,里面不调用父类方法,即父类eat被覆盖了.
		eat : function(){
			alert('Duke is eating..');
		},
		
		//新增一个say方法,显示当前已经初始化的Duke类实例数量.
		say : function(){
			alert('the number of Duke instances is '+static_instance_counter);
		}
	};
});


var DukeChild = CC.create(Duke, function(superclass){
	return {
		move : function(){
			this.footprint = this.footprint + '++++++++++++=';
			superclass.move.call(this);
		},
		
		say : function(){
			alert(this.msg || '');
		}
	};
});

 

看看效果如何

 

(function test() {
	var animal = new Animal();
	animal.eat();
	animal.move();
	
	var dukeA = new Duke();
	dukeA.eat();
	dukeA.move();
	dukeA.say();
	
	var dukeB = new Duke();
	dukeB.eat();
	dukeB.move();
	dukeB.say();
	
	var dukeC = new DukeChild({msg : 'I am a child of duke.'});
	dukeC.move();
	dukeC.say();
})();
 

 

这是个人的一点经验之谈,望能抛砖引玉.

 

论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics