日期:2014-05-16  浏览次数:20418 次

[基础]JavaScript中的面向对象(个人学习笔记) 二
   有了第一篇的一些对对象的了解,后面学习如果来定义一个类也可以说成定义一个对象就会容易许多,废话就这么多了,继续开始学习。首先从定义对象的方式介绍。
1、工厂方式
    当要定义一个名为“人”的类时,可以简单的设想我们需要为这个人设置属性:名字还有性别。可以简单地用一下代码实现:
  var oPerson = new Object;
  oPerson.name = "yzl";
  oPerson.sex = "female";
  oPerson.sayInfo = function(){
    alert("name : " + this.name + " ,sex :" + this.sex);
  };

    这只是创建了一个简单的对象,那么如果要创建多个对象,现在就不满足了,所以引入了工厂函数,进行改装之后的代码:
  function createPerson(){
    var oPerson = new Object;
    oPerson.name = "yzl";
    oPerson.sex = "female";
    oPerson.sayInfo = function(){
      alert(" name : " + this.name + " ,sex :" + this.sex);
    };
    return oPerson;
  };
  var p1 = createPerson();
  p1.sayInfo();
  p1.name = "gcm";
  p1.sex = "female";
  p1.sayInfo(); 

   到这里看起来好像真的创建了一个人了,但是很明显能够看到不足,所有的实例的属性必须在对象创建了之后进行具体的修改,不能在对象建立时就根据具体情况创建对象。于是我们对代码进行了相关的修改,具体如下:
function createPerson(sName,sSex){
	var oPerson = new Object;
  oPerson.name = sName;
  oPerson.sex = sSex;
  oPerson.sayInfo = function(){
		alert("name : " + this.name + " ,sex :" + this.sex);
	};
	return oPerson;
};

var p1 = createPerson("yzl","male");
var p2 = createPerson("gcm","female");
p1.sayInfo();
p2.sayInfo(); 

    代码修改到这里基本上已经满足了需求,能够根据具体的情况创建不同的人。可是。。。(写到这里我突然感觉如此多的转折怎么跟春晚的魔术的托一样,大家原谅啊~),可是考虑一下内存,我们应该能够看到每次createPerson都会创建一个sayInfon()函数,即每个person都拥有自己对应的sayInfo方法,但是方法的内容都是相同的,所以应该选择更好的方式。考虑到EMCAScript是支持闭包的,所以可以将函数的定义放到类之外,让后让类的属性指向该方法的引用:
  function sayInfo(){
    alert("name : " + this.name + " ,sex :" + this.sex);
  }

  function createPerson(sName,sSex){
    var oPerson = new Object;
    oPerson.name = sName;
    oPerson.sex = sSex;
    oPerson.sayInfo = sayInfo;
    return oPerson;
  }; 
  var p1 = createPerson("yzl","male");
  var p2 = createPerson("gcm","female");
  p1.sayInfo();
  p2.sayInfo(); 

   工厂模式已经满足了需求,从不断地修改代码的过程可以看出工厂模式存在的各种问题,于是便有了更加接近java代码的构造函数方式。

2、构造函数方式
    构造函数方式和工厂模式非常的相似,只是在定义类的时候直接使用了类名,而参数的赋值使用了关键字this,也不需要返回值。直接看代码:
  function sayInfo(){
    alert("name : " + this.name + " ,sex :" + this.sex);
  }
  function Person(sName,sSex){
    this.name = sName;
    this.sex = sSex;
    this.sayInfo = sayInfo; 
  }
  var p1 = new Person("yzl","male");
  p1.sayInfo();

    这种方式其实很像工厂模式,没有多少说的东西,继续下一种定义类的方式。

3、原型方式
    该方式利用了对象的prototype属性,可以把它看成新对象所依赖的原型。采用这种方式来定义一个类的方法一般是先采用空的构造函数来设置类名,然后所有的属性和方法都被直接赋予prototype属性。参照以下代码:
  function Person(){
  }
  Person.prototype.name = "yzl";
  Person.prototype.sex = "male";
  Person.prototype.sayInfo = function(){
    alert("name : " + this.name + " ,sex :" + this.sex);
  };
  var p1 = new Person();
  p1.sayInfo();
  p1.name = "gcm";
  p1.sex = "female";
  p1.sayInfo(); 

   采用原型方式创建实例时,原型的所有属性都被立即赋予要创建的对象,所有属性看起来都属于同一个对象,其中的sayInfo方法都指向同一个引用。使用该方法定义类可以使用instranceof运算符检查实例指向的类型。
  alert(p1 instanceof Person); //true

采用原型方式存在了一个前面提到的劣势,某个实例的属性要等对象创建了之后才能根据具体的情况进行修改,这都是可以接受的。可是由于所有属性同时指向同一个引用,存在一个严重的问题,见代码:
  function Person(){
  }
  Person.prototype.name = "yzl";
  Person.prototype.sex = "male";
  Person.prototype.friends = new Array("ymj","gcm");
  Person.prototype.sayInfo = function(){
    alert("name : " + this.name + " ,sex :" + this.sex);
  };
  var p1 = new Person();
  var p2 = new Person();
  p1.friends.push("hl");
  alert(p2.friends); // ym