提要

1、原始方式

2、工厂方式

3、构造方式

4、原型方式

5、构造原型混合方式

6、动态原型方式

7、混合工厂方式

目前使用最广泛的是混合的构造函数/原型方式。此外动态原型方法也很流行在功能上与构造函数/原型方式等价。可以采用这两种方式中的任何一种。不过不要单独使用经典的构造函数或原型方式因为这样会给代码引入问题。

1、原始方式

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

缺点可能需要创建多个 car 的实例。


2、工厂方式

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 oCar1 = createCar();
var oCar2 = createCar();

2使用带参数的工厂函数

function createCar(sColor,iDoors,iMpg) {
  var oTempCar = new Object;
  oTempCar.color = sColor;
  oTempCar.doors = iDoors;
  oTempCar.mpg = iMpg;
  oTempCar.showColor = function() {
    alert(this.color);
  };
  return oTempCar;
}

var oCar1 = createCar("red",4,23);
var oCar2 = createCar("blue",3,25);

oCar1.showColor();		//输出 "red"oCar2.showColor();		//输出 "blue"


3在工厂函数外定义对象的方法

虽然 ECMAScript 越来越正式化但创建对象的方法却被置之不理且其规范化至今还遭人反对。一部分是语义上的原因它看起来不像使用带有构造函数 new 运算符那么正规一部分是功能上的原因。功能原因在于用这种方式必须创建对象的方法。前面的例子中每次调用函数 createCar()都要创建新函数 showColor()意味着每个对象都有自己的 showColor() 版本。而事实上每个对象都共享同一个函数。

有些开发者在工厂函数外定义对象的方法然后通过属性指向该方法从而避免这个问题

function showColor() {
  alert(this.color);
}function createCar(sColor,iDoors,iMpg) {
  var oTempCar = new Object;
  oTempCar.color = sColor;
  oTempCar.doors = iDoors;
  oTempCar.mpg = iMpg;  oTempCar.showColor = showColor;
  return oTempCar;
}

var oCar1 = createCar("red",4,23);
var oCar2 = createCar("blue",3,25);

oCar1.showColor();		//输出 "red"oCar2.showColor();		//输出 "blue"


在上面这段重写的代码中在函数 createCar() 之前定义了函数 showColor()。在 createCar() 内部赋予对象一个指向已经存在的 showColor() 函数的指针。从功能上讲这样解决了重复创建函数对象的问题但是从语义上讲该函数不太像是对象的方法。

所有这些问题都引发了开发者定义的构造函数的出现。


3、构造函数方式

创建构造函数就像创建工厂函数一样容易。第一步选择类名即构造函数的名字。根据惯例这个名字的首字母大写以使它与首字母通常是小写的变量名分开。除了这点不同构造函数看起来很像工厂函数。请考虑下面的例子

function Car(sColor,iDoors,iMpg) {  this.color = sColor;  this.doors = iDoors;  this.mpg = iMpg;  this.showColor = function() {
    alert(this.color);
  };
}

var oCar1 = new Car("red",4,23);
var oCar2 = new Car("blue",3,25);

与工厂函数相似也可以用外部函数重写构造函数。


4、原型方式

该方式利用了对象的 prototype 属性可以把它看成创建新对象所依赖的原型。

这里首先用空构造函数来设置类名。然后所有的属性和方法都被直接赋予 prototype 属性。我们重写了前面的例子代码如下

function Car() {
}

Car.prototype.color = "blue";
Car.prototype.doors = 4;
Car.prototype.mpg = 25;
Car.prototype.showColor = function() {
  alert(this.color);
};

var oCar1 = new Car();
var oCar2 = new Car();


注1使用这种方式还能用 instanceof 运算符检查给定变量指向的对象的类型。因此下面的代码将输出 TRUE

alert(oCar1 instanceof Car);	//输出 "true"

注2原型方式不能带参数。

注3真正的问题出现在属性指向的是对象而不是函数时。函数共享不会造成问题但对象却很少被多个实例共享

function Car() {
}

Car.prototype.color = "blue";
Car.prototype.doors = 4;
Car.prototype.mpg = 25;Car.prototype.drivers = new Array("Mike","John");Car.prototype.showColor = function() {
  alert(this.color);
};

var oCar1 = new Car();
var oCar2 = new Car();oCar1.drivers.push("Bill");alert(oCar1.drivers);	//输出 "Mike,John,Bill"alert(oCar2.drivers);	//输出 "Mike,John,Bill"


5、混合的构造函数/原型方式

联合使用构造函数和原型方式就可像用其他程序设计语言一样创建对象。这种概念非常简单即用构造函数定义对象的所有非函数属性用原型方式定义对象的函数属性方法。结果是所有函数都只创建一次而每个对象都具有自己的对象属性实例。

我们重写了前面的例子代码如下

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

Car.prototype.showColor = function() {
  alert(this.color);
};

var oCar1 = new Car("red",4,23);
var oCar2 = new Car("blue",3,25);

oCar1.drivers.push("Bill");

alert(oCar1.drivers);	//输出 "Mike,John,Bill"alert(oCar2.drivers);	//输出 "Mike,John"

所有的非函数属性都在构造函数中创建意味着又能够用构造函数的参数赋予属性默认值了。

      因为只创建 showColor() 函数的一个实例所以没有内存浪费。

     此外给 oCar1 的 drivers 数组添加 "Bill" 值不会影响到 oCar2 的数组所以输出这些数组的值时oCar1.drivers 显示的是 "Mike,John,Bill"而 oCar2.drivers 显示的是 "Mike,John"。

      因为使用了原型方式所以仍然能利用 instanceof 运算符来判断对象的类型。



6、动态原型方法

动态原型方法的基本想法与混合的构造函数/原型方式相同即在构造函数内定义非函数属性而函数属性则利用原型属性定义。唯一的区别是赋予对象方法的位置。下面是用动态原型方法重写的 Car 类

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;
  }
}


7、混合工厂方式

这种方式通常是在不能应用前一种方式时的变通方法。它的目的是创建假构造函数只返回另一种对象的新实例。

这段代码看起来与工厂函数非常相似

function Car() {  

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

  return oTempCar;
}

由于在 Car() 构造函数内部调用了 new 运算符所以将忽略第二个 new 运算符位于构造函数之外在构造函数内部创建的对象被传递回变量 car。

这种方式在对象方法的内部管理方面与经典方式有着相同的问题。强烈建议除非万不得已还是避免使用这种方式。


参考文献

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