1、概述:
尽管object构造函数或者字面量对象都可以创建单个对象,但是该方法存在一个明显的不足:即一个接口创建了多个对象,产生了代码的重复,为此,引入工厂模式(抽象类创建具体对象的过程)的派生方法,来解决多个对象相似的问题:
2、工厂模式:
实现方法:用函数来封装特定接口创建对象的细节,如下:
function createProduct(id,name,price){
var obj = new Object();
obj.id = id;
obj.name = name;
obj.price = price;
obj.produceProcess = function(){
alert(“First…Second…”);
};
return obj;
}
var productOne = new createProduct(“1″,”Apple”,”1.20$”);
var productTwo = new createProduct(“2″,”Peach”,”0.50$”);
存在问题:解决了多个对象相似的问题,关于对象识别,即确认对象类型,没有给出解决办法,进而引入构造函数模式:
3、构造函数模式:
ECMAScript(European Computer Manufacturers Association Script)中的构造函数可以用来创建特定类型的对象,也可以自定义构造函数,进而自定义对象类型的属性与方法,重写工厂模式的例子如下:
function Product(id,name,price){
this.id = id;
this.name = name;
this.price = price;
this.produceProcess = function(){
alert(“First…Second…”);
};
}
var productOne = new Product(“1″,”Apple”,”1.20$”);
var productTwo = newProduct(“2″,”Peach”,”0.50$”);
自定义模式意味着可以把它的实例标识为一种特定的类型;
存在问题:每个方法都要在每个实例中创建一遍,解决方案:将函数的定义转移到构造函数的外部,将produceProcess属性设置为全局的produceProcess函数;如下:
function Product(id,name,price){
this.id = id;
this.name = name;
this.price = price;
this.produceProcess = produceProcess;
}
function produceProcess (){
alert(“First…Second…”);
};
不足之处:全局变量只能被某个对象所调用,不能“全局化”,定义多个方法,同时即需要定义多个全局变量;引入原型模式解决该问题;
4、原型模式:
每一个函数都具有原型(prototype)属性,该属性是一个指针,指向一个对象,该对象包含由特定类型的所有实例共享的属性和方法,即一个原型对象可以让所有对象实例来共享它所包含的属性和方法,从而不必在构造函数中定义对象实例的信息,而是将这些信息添加到原型对象中去:具体实现如下:
function Product(){}
Product.prototype.id = “1”;
Product.prototype.name = “Apple”;
Product.prototype.price = “1.20$”;
Product.prototype.produceProcess = function(){
alert(“First…Second…”);
};
var productOne = new Product();
productOne.produceProcess(); //First…Second…
var productTwo = new Product();
productTwo .produceProcess(); //First…Second…
存在问题:这种共享对于函数来说,比较合适,对于包含引用类型值的属性来说,存在很大问题,如果要给一个包含引用类型值添加或者删除一个它的属性,则原型模式的优点变成了它的缺点,使得这种操作无效,如下:
function Product(){}
Product.prototype = {
constructor: Product,
id : “1”,
name : “Apple”,
price : “1.20$”,
contained : [“seed”,”pulp”], //苹果包含种子和果肉,但是………
produceProcess : function(){
alert(“First…Second…”);
};
}
var productOne = new Product();
var productTwo = new Product();
//但是第二个苹果里面还有一只虫子呢,怎么办?
productTwo.contained .push(“worm”);
alert(productOne.contained ); // “seed”,”pulp”,”worm“; 第一个苹果怎么也有虫子?
alert(productTwo.contained ); // “seed”,”pulp”,”worm”;
alert(productOne.contained === productTwo.contained ); // true 你看,这两苹果都有虫子了
因为不能让苹果都坏掉,所以一般采用构造函数模式和原型模式组合的方法来解决这种问题:
5、组合模式:
组合模式中:构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性,所以每个实例都会有自己的一份实例属性的副本,同时,共享着对方法的引用,最大程度的节省了内存,如下:
function Product(id,name,price){
this.id = id;
this.name = name;
this.price = price;
this.contained = [“seed”,”pulp”];
}
Product.prototype = {
constructor : Product;
produceProcess : function(){
alert(“First…Second…”);
};
};
var productOne = new Product(“1″,”Apple”,”1.20$”);
var productTwo = newProduct(“2″,”Peach”,”0.50$”);
productTwo.contained.push(“worm”); //虫虫又来了
alert(productOne.contained ); // “seed”,”pulp”;
alert(productTwo.contained ); // “seed”,”pulp”,”worm”; 虫虫只在这里吃苹果
alert(productOne.contained === productTwo.contained ); //false 苹果可以吃了
alert(productOne.produceProcess === productTwo.produceProcess ); // true
好了,这种最流行,最愉快的对象创建方式就这样愉快结束!
6、补遗:
还有几种模式,一并介绍:
- 动态原型模式:该方法关于是否初始化原型,即使用某个方法前应当对它进行存在性判断,如果不存在,创建一个就行,而且对原型的修改,即会体现在实例中;
- 寄生构造模式和稳妥构造使用比较少,详细说明,此处暂略!
7、参考资料:
- 《JavaScript高级程序设计》 Nicholas C.Zakas 著 人民邮电出版社;