[基础]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); // ymj gcm hl

   代码中明明只修改了p1的friends属性,可是p2的friends属性也产生了变化,这是一个非常严重的错误。个人感觉就跟这种方式是线程不安全的,多个线程访问就会出错。经过这么多的修改,于是提出了更加合理的方式: 联合使用构造函数和原型方式。

4、联合使用构造函数和原型方式
这种方式其实非常简单,基本的思路就是 采用构造函数来定义类的属性,采用原型方式来声明类的方法。这样就可以实现所有的对象独立占有自己的属性,而且保证了所有的方法只创建一次,代码如下:
  function Person(sName,sSex){
    this.name = sName;
    this.sex = sSex;
    this.friends = new Array();
  }

  Person.prototype.makeFriend = function(newFriend){
    this.friends.push(newFriend);
  }

  Person.prototype.sayInfo = function(){
    alert("name : " + this.name + " ,sex :" + this.sex + ", and has  friends :" + this.friends );
  };
  var p1 = new Person("yzl","male");
  var p2 = new Person("gcm","female");
  p1.makeFriend("zxt");
  p2.makeFriend("gr");
  p1.sayInfo();
  p2.sayInfo();

   上面的这种方式已经很像java中创建一个对象了,这也是ECMAScript主要采用的方式。虽然还有其他几种创建类的方式,但是感觉记住最有用就可以了,其他的方式不再继续介绍,最后补充一种实现javascript重载的方法。首先必须先先了解一下Javascript中一个很重要的属性 arguments,它可以记录下在调用函数时所传递的参数,也就是通过它来实现函数的重载,代码如下:(由于最近忙于换工作所以写的比较慢请大家原谅)
  function Person(sName,sSex){
    switch(arguments.length){
	case 0 :
	  this.name = "unKnown";
	  this.sex = "unKnown";
	  break;
	case 1 :
	  this.name = sName;
	  this.sex = "unKnown";
	  break;
	case 2 :
	  this.name = sName;
	  this.sex = sSex;
	  break;			
	};
  }

  Person.prototype.sayInfo = function(){
    alert("name : " + this.name + " ,sex :" + this.sex);
  };

  var p1 = new Person("yzl","male");
  var p2 = new Person("gcm");
  var p3 = new Person();
  p1.sayInfo();
  p2.sayInfo();
  p3.sayInfo();

你可能感兴趣的:(JavaScript,prototype)