javascript面向对象实现java常用的1StringBuffer,Map,Collection,List,Set

javascript面向对象实现java常用的StringBuffer,Map,Collection,List,Set

1前言

       在开发中,我们做java后台开发的,普遍都是擅长后端语言,对于javascript却不甚理想。偶尔我们会去w3c看看javascript的基本语法,但是每次都是学习不到位,用起来感觉总是马马马虎虎。在最近的开发中,这个老大难的问题又出现在了我面前。一开始,继续忽略,一张手就是定义了一个function xxx(){}。但是噩梦往往都是因为懒惰造成的,由于开发的需要,某个页面的js文件多大1000 line+,方法至少也是10+了。作为一个开发人员,目睹着自己的代码,尽然会超出自己的控制,显然让人不甘心啊,于是乎,重构了很多代码,但是依旧木有使用到面向对象的编程范式。在折腾了几天后,终于下定决心,使用面向对象的技术来重写整个js文件的代码。

要想用好面向对象,那么就需要理解面向对象的编程思想,作为一个后台开发人员,就不多说了,面向对象最主要的四个特性:继承,多态,封装,抽象。更加详细的内容,请参考面向对象的相关数据。

java中,我们使用class来定义一个类,用于规范某种对象的特性(属性,方法),当该class定义好之后,就可以使用new ClassXxx()的方式来实例化一个对象,并且所有同一个class生成的对象都具备着一致的特性(属性,方法),后期不能动态的给对象添加属性与方法。对于这种后期模式的开发语言来说,优势非常明显,那就是所有对象的属性与方法都确定了下来,并且可以通过预编译,将class转换成二进制的机器码,所以在运行效率上会比较有优势。但是缺点也同样非常明显,比如需要编译源码,不能动态添加属性或者方法,开发效率较低等。


2面向对象技术

  在javascript中,有如下几种数据类型,分别是:undefined,boolean,string,number,object,function。这里面,我们只关注object(对象)。对象是javascript的基本数据类型,将很多值聚合在一起,可以通过名字访问这些值。比如:{name:骆宏, age:20}。每一个对象除了保持自有的属性,通常还可以从一个称为原型的对象那里继承属性,通常情况下,会继承原型的方法。Javascript对象是动态的,可以动态的添加属性,或者删除属性,与java这类语言类型不一样。


2.1javascript的继承

      在java中,所有的对象都继承java.lang.Object,并且是单根继承。在javascript中也类型,所有的对象原型都继承Object.prototype对象。在这里面,我们需要特别注意的是:每个对象都引用着一个原型对象,比如Student对象引用着一个Student.prototype,并且这个Student.prototype同时也引用着Object.prototype,我们称之为javascript的原型链。下面我们假设有如下的继承关系:

javascript面向对象实现java常用的1StringBuffer,Map,Collection,List,Set_第1张图片

  在这里面,正方形对象继承了矩形,矩形继承了多边形,多边形继承了形状,而形状继承了Object。于是正方形对象就可以获得从Object.prototype+形状.prototype+多边形.prototype+矩形.prototype的所有属性(除了一些不可以继承的属性)。一个对象在寻找属性时,会首先查找自身是否有这个属性,如果找不到,就会去到该对象的原型中选中,如果也找不到,就会递归的往Object.prototype方向去寻找该属性。我们称之为该对象的继承关系的原型链(非严肃的说法,官方定义请查阅w3c的javascript规范)。


2.2javascript对象的实例化

1工厂方式
function createCar() {
  var oTempCar = new Object;  //实例化一个对象
  oTempCar.color = "blue";  //给对象赋值
  oTempCar.doors = 4;
  oTempCar.mpg = 25;
  oTempCar.showColor = function() {  //属性的值可以为方法
    alert(this.color);
  };
  return oTempCar;  //返回对象
}
使用方式:var car = createCar();

2构造函数方式
function Car(sColor,iDoors,iMpg) {
  this.color = sColor;  //使用this来表示当前的对象
  this.doors = iDoors;
  this.mpg = iMpg;
  this.showColor = function() {  //每次创建对象时,都需要创建一个匿名函数
    alert(this.color);
  };
}
使用方式:var car = new Car(“红色”, “是”, “是”);

3原型方式

  在方式中2,每次创建一个Car时,都是实例化this.showColor,但是通过观察,我们可以发现,这其实是一个方法,只需要实例化一次即可。为了改进上述代码,使用原型的方式重写上诉的代码,如下:

function Car(sColor,iDoors,iMpg) {
  this.color = sColor;
  this.doors = iDoors;
  this.mpg = iMpg;
  this.drivers = new Array("Mike","John");
}

Car.prototype.showColor = function() {  //将该方法放在原型的属性上,所有的Car                       
                                  //对象都从这个原型对象继承该属性
  alert(this.color);
};

4动态原型方式

function Car(sColor,iDoors,iMpg) {
  this.color = sColor;
  this.doors = iDoors;
  this.mpg = iMpg;
  this.drivers = new Array("Mike","John");
  
  if (typeof Car._initialized == "undefined") {  //给构造方法添加一个属性,记录下第
                                       //一次实例化
    Car.prototype.showColor = function() {
      alert(this.color);
    };
	
    Car._initialized = true;   //标志着该对象已经实例化过了
  }
}

5混合工厂模式

function Car() {
  var oTempCar = new Object;
  oTempCar.color = "blue";
  oTempCar.doors = 4;
  oTempCar.mpg = 25;
  oTempCar.showColor = function() {
    alert(this.color);
  };

  return oTempCar;
 

  在实际开发中,那么我们该使用哪种方式呢?在目前,原型方式与动态原型方式都比较流行,个人推荐动态原型的方式,因为可以将所有代码集中在构造函数内部,避免代码分散在多个地方。


2.3使用javascript技术模拟java中常用的类

StringBuffer

function StringBuffer(){
	this.strs = [];  //用数组来缓存所有的内容

	if(typeof StringBuffer._initialized == "undefined"){
		StringBuffer._initalized = true;  //已经初始化过了,不需要重复初始化

		StringBuffer.prototype.append = function(str){
			this.strs.push(str);
			return this;
		};
        //覆写toString,避免使用Object.toString
		StringBuffer.prototype.toString = function(seperator){
			if(!seperator){
				seperator = "";
			}
			return this.strs.join(seperator);
		};
	}
}


Map

function Map(){
	this.elements = new Array();  

	if(typeof Map._initialized == "undefined"){
		Map._initialized = true;  //记录下已经实例化的标识符

		Map.prototype.size = function(){
			return this.elements.length;
		};

		Map.prototype.isEmpty = function(){
			return this.elements.length == 0;
		};

		Map.prototype.clear = function(){
			this.elements = new Array();
		};

		Map.prototype.put = function(key, value){
			if(key == null || value == null) throw TypeError(); 

			var exists = false;

			if(this.isEmpty()){
				this.elements.push({"key": key, "value": value});
			}else{  
				for(var i=0; i<this.elements.length; i++){
					if(this.elements[i].key == key){  //override
						this.elements[i].value = value;
						exists = true;
					}
				}

				if(!exists){
					this.elements.push({"key": key, "value": value});
				}
			}
		};

		Map.prototype.get = function(key){
			if(key == null) throw TypeError();

			if(this.isEmpty()) return null;
			for(var i=0; i<this.elements.length; i++){
				if(this.elements[i].key == key) return this.elements[i].value;
			}
			return null;
		};

		Map.prototype.remove = function(key){
			if(key == null) throw TypeError();

			if(this.isEmpty()) return false;

			for(var i = 0; i < this.elements.length; i++) {     
				if(this.elements[i].key == key) {     
					this.elements.splice(i, 1);     //delete elements[i] 
					return true;     
				}     
			}  
			return false;
		};

		Map.prototype.containsKey = function(key){
			if(key == null) throw TypeError();

			if(this.isEmpty()) return false;

			for(var i = 0; i < this.elements.length; i++) {     
				if(this.elements[i].key == key) {     
					return true;     
				}     
			}  

			return false;
		};

		Map.prototype.containsValue = function(value){
			if(value == null) throw TypeError();

			if(this.isEmpty()) return false;

			for(var i = 0; i < this.elements.length; i++) {     
				if(this.elements[i].value == value) {     
					return true;     
				}     
			}  

			return false;
		};

		Map.prototype.keys = function(){
			if(this.isEmpty()) return [];

			var keys = new Array();
			for(var i=0; i<this.elements.length; i++){
				keys.push(this.elements[i].key);
			}

			return keys;
		};

		Map.prototype.values = function(){
			if(this.isEmpty()) return [];

			var values = new Array();
			for(var i=0; i<this.elements.length; i++){
				values.push(this.elements[i].value);
			}

			return values;
		};

		Map.prototype.toString = function(){
			if(this.isEmpty()) return "[{}]";

			var sb = new StringBuffer();
			sb.append("[");
			for(var i=0; i<this.elements.length; i++){
				sb.append("{").append(this.elements[i].key).append(",").append(this.elements[i].value).append("}");
				if(i != this.elements.length - 1){
					sb.append(",");
				}
			}
			sb.append("]");

			return sb.toString();
		};
	}
}

Collection

function Collection(){
	this.elements = new Array();
}

Collection.prototype.add = function(obj){
	if(obj == null) throw TypeError();
	this.elements.push(obj);
	return true;
};

Collection.prototype.addAll = function(objs){
	if(objs == null) throw TypeError();
	this.elements = this.elements.concat(objs);
	return true;
};

Collection.prototype.get = function(index){
	this._checkIndex(index);

	if(this.isEmpty()) return null;

	return this.elements[index];
};

/**
 * 删除下表为index的元素
 * */
Collection.prototype.removeIndex = function(index){
	this._checkIndex(index);
	this.elements.splice(index, 1);     //delete elements[i-1]
};

/**
 * 删除某个元素
 * 这里面会从下表0开始便利,删除第一个匹配的元素
 * */
Collection.prototype.removeObj = function(obj){
	if(this.isEmpty()) return false;
	
	for(var i=0; i<this.elements.length; i++){
		if(this.elements[i] == obj){
			this.elements.splice(i-1, 1);     //delete elements[i]
		}
	}
};

Collection.prototype.contains = function(obj){
	if(obj == null) throw TypeError();

	if(this.isEmpty()) return false;

	for(var i=0; i<this.elements.length; i++){
		if(this.elements[i] == obj){
			return true;
		}
	}

	return false;
};

Collection.prototype.containsAll = function(objs){
	if(objs == null) throw TypeError();
	if(objs.length > this.elements.length) return false;

	for(var i=0; i<objs.length; i++){
		if(this.contains(objs[i]) == false){
			return false;
		}
	}

	return true;
};

Collection.prototype.size = function(){
	return this.elements.length;
};

Collection.prototype.isEmpty = function(){
	return this.elements.length == 0;
};

Collection.prototype.clear = function(){
	this.elements = new Array();
};

Collection.prototype._checkIndex = function(index){
	if(index < 0 || index >= this.elements.length){
		throw new Error("index must be in [0, " + this.elements.length + ")");
	}
};

/**
 * 默认输入的格式如下:
 * [element1,element2,element3,...,elementx]
 * 
 * 如果客户端需要获取类型数组join的效果,直接传入分隔符即可
 * 比如collection的内容为:a,b,c,默认的toString为[a,b,c],如果想要获得:abc,传入"";想要获得a,b,c,传入",",想要获得a b c,传入" "
 * 
 * */
Collection.prototype.toString = function(separator){
	if(this.isEmpty()) return "";

	if(separator != null){
		return this.elements.join(separator);
	}else{
		var sb = new StringBuffer();
		sb.append("[");
		for(var i=0; i<this.elements.length; i++){
			sb.append(this.elements[i]);
			if(i != this.elements.length - 1){
				sb.append(",");
			}
		}
		sb.append("]");
		return sb.toString();
	}
};

List

/**
 * Collection的子类
 * 提供了如下几个增强方法:
 * set(index, obj)
 * subList
 * */
function List(){
    this.elements = new Array();  //这个属性不能使用Collection中的elements,因为原型对象都是collection,那样子会出现bug
}

//List.prototype修改为new Collection对象,等价于继承了Collection的所有//属性
List.prototype = new Collection(); 

/**
 * 在第几个位置插入元素
 * */
List.prototype.set = function(index, obj){
	this._checkIndex(index);
	this.elements.splice(index, 0, obj);     //add elements[i]
	return true;
};

/**
 * 获取子链表
 * */
List.prototype.subList = function(start, end){
	this._checkIndex(start);
	this._checkIndex(end);
	if(start >= end){
		throw new Error("start must be less than end.");
	}

	var subList = new Array();
	var index = 0;
	for(var i=start; i<end; i++){
		subList[index++] = this.elements[start];
	}

	return subList;
};

Set

/**
 * Collection子类
 * 因为Set中,元素不能重复,所以这里面override了Collection的add,addAll方法
 * */
function Set(){
	this.elements = new Array();  //这个属性不能使用Collection中的elements,因为原型对象都是collection,那样子会出现bug
}
Set.prototype = new Collection();  //继承Collection

/**
 * 因为Set不允许重复元素存在,所以这里面需要override add,addAll方法
 * */
Set.prototype.add = function(obj){
	if(this.contains(obj)){
		return false;
	}else{
		this.elements.push(obj);
		return true;
	}
};

Set.prototype.addAll = function(objs){
	if(objs == null) throw new TypeError("objs can't be null");
	
	for(var i=0; i<objs.length; i++){
		if(this.contains(objs[i])){
			continue;
		}else{
			this.elements.push(objs[i]);
		}
	}
};

/**
 * 在第几个位置插入元素
 * */
Set.prototype.set = function(index, obj){
	this._checkIndex(index);
	if(this.contains(obj)){
		return false;
	}else{
		this.elements.splice(index, 0, obj);     //add elements[i]
		return true;				
	}
};


3总结

     每一个后端开发人员,都应该详细的解读下javascript,而不是大概知道怎样。在实战中,可以提高开发人员的开发效率,代码的可维护性也变得不再那么狼狈不堪。下次再写javascript代码时,当你一顺手写下function xxx(){}时,请再思考下吧。


关于面向对象技术,大家可以参考: 

1 Jquery源码分析

http://www.cnblogs.com/aaronjs/p/3279314.html

 

2 w3c面向对象技术

http://www.w3school.com.cn/js/pro_js_object_oriented.asp

 

3.IBM关于javascript面向对象的讲解

http://www.ibm.com/developerworks/cn/web/1304_zengyz_jsoo/








你可能感兴趣的:(javascript面向对象实现java常用的1StringBuffer,Map,Collection,List,Set)