Object构造函数或对象字面量都可以用来创建单个对象,但是使用这些方式用同一个接口创建很多对象,会产生大量的重复代码。为了解决这个问题,工厂模式的一种变体开始被使用。

1、工厂模式

工厂模式抽象了创建具体对象的过程。因为ECMAScript无法创建类,开发人员发明了一种函数,用函数来封装以特定接口创建对象的细节,如:
1 function createPerson(name, age, job){
 2     var o = new Object();
 3     o.name = name;
 4     o.age = age;
 5     o.job = job;
 6     o.sayName = function(){
 7         alert(this.name);
 8     }
 9     return o;
10 }
11 var person1 = createPerson("Anna", 26, "Software Engineer");
12 var person2 = createPerson("Lily", 24, "Teacher");
可以无数次调用createPerson()函数,它每次都会返回一个包含三个属性一个方法的对象。

工厂模式虽然可以创建多个对象,但无法识别对象的类型。

2、构造函数模式

构造函数可创建特定类型的对象,包括原生构造函数(如Object、Array)和自定义构造函数。

自定义构造函数,定义自定义对象类型的属性和方法,如:
1 function Person(name, age, job){    //惯例,构造函数以大写字母开头,非构造函数以小写字母开头
 2     this.name = name;   //构造函数直接将属性和方法赋给了this对象
 3     this.age = age;
 4     this.job = job;
 5     this.sayName = function(){
 6         alert(this.name);
 7     };
 8 }
 9 //使用new操作符创建Person的新实例
10 var person1 = new Person("Anna", 26, "Software Engineer");
11 var person2 = new Person("Lily", 24, "Teacher");
使用new操作符调用构造函数会经理以下4个步骤:

    (1)创建一个对象;
    (2)将构造函数的作用域赋值给新对象(因此this就指向了这个新对象);
    (3)执行构造函数中的代码(为这个新对象添加属性)。
    (4)返回新对象。

创建的实例对象有一个constructor(构造函数)属性,该属性指向Person,如:

alert(person1.constructor == Person);  //true

对象的constructor属性最初用来标识对象类型。但是检测对象类型,instanceof更可靠。例子中创建的对象既是Object的实例,也是Person的实例。如:

alert(person1 instanceof Object);  //true 所有的对象均继承自Object
alert(person1 instanceof Person);  //true

构造函数模式优于工厂模式的地方:自定义的构造函数的实例将来可以标识为一种特定的类型。

构造函数缺点:每个方法都要在每个实例上重新创建一遍。但是,创建多个完成同样任务的Function实例没有必要。

3、原型模式

创建的每一个函数都有一个prototype(原型)属性,是一个指针,指向一个对象,这个 对象包含可以由特定类型的所有实例共享的属性和方法。

使用原型的对象的好处:可以让所有对象实例共享它所包含的属性和方法。
1 function Person(){
 2 }
 3 Person.prototype.name = "Anna";
 4 Person.prototype.age = 29;
 5 Person.prototype.job = "Doctor";
 6 Person.prototype.sayName = function(){
 7     alert(this.name);
 8 };
 9 var person1 = new Person();
10 person1.sayName();  // "Anna"
11 var person2 = new Person();
12 person2.sayName();  // "Anna"
13 alert(person1.sayName == person2.sayName);  //true
4、组合使用构造函数模式和原型模式

使用最广泛、认同度最高的一种建立自定义类型的方法,定义引用类型的默认模式。

构造函数模式用来定义实例属性,原型模式用来定义方法和共享的属性。

每个实例都会有自己的一份实例属性的副本,但同时共享着对方的引用,最大限度地节省了内存。

支持向构造函数传递参数。
1 function Person(name, age, job){
 2     this.name = name;
 3     this.age = age;
 4     this.job = job;
 5     this.friends = ["Shelby", "Court"];
 6 }
 7 Person.prototype = {
 8     constructor: Person,
 9     sayName: function(){
10         alert(this.name);
11     }
12 }
13 var person1 = new Person("Anna", 26, "Software Engineer");
14 var person2 = new Person("Greg", 27, "Doctor");
15 
16 person1.friends.push("Van");
17 alert(person1.friends);  // "Shelby,Court,Van"
18 alert(person2.friends);  // "Shelby,Court"
19 alert(person1.friends === person2.friends);  //  false
20 alert(person1.sayName === person2.sayName);  //  true
5、动态原型模式

把所有信息都封装在了构造函数中,通过在构造函数中初始化原型(仅在必要的情况下),保持了同时使用构造函数和原型的优点。
1 function Person(name, age, job){
 2     // 属性
 3     this.name = name;
 4     this.age = age;
 5     this.job = job;
 6     // 方法
 7     if(typeof this.sayName != "function"){  //可以检查任何属性或方法之一即可
 8         Person.prototype.syaName = function(){
 9             alert(this.name);
10         };
11     }
12 }
13 var friend = new Person("Anna", 27, "Software Engineer");
14 friend.sayName();
这里对原型所做的修改,能够立即在所有实例中得到反映。

注:动态原型模式,不能使用对象字面量重写原型。如果已经创建实例后重写原型,会切断现有实例与新原型之间的联系。

6、寄生构造函数模式

创建一个函数,封装创建对象的代码,返回新创建的对象。
1 function Person(name, age, job){
 2     var o = new Object();
 3     o.name = name;
 4     o.age = age;
 5     o.job = job;
 6     o.sayName = function(){
 7         alert(this.name);
 8     };
 9     return o;  //构造函数不返回值时,默认返回新对象实例;return返回值时,可以重写调用构造函数时返回的值
10 }
11 var friend = new Person("Anna", 27, "Software Engineer");
12 friend.sayName();  // "Anna"
假设创建一个有额外方法的特殊数组,由于不能直接修改Array构造函数,因此可以使用这个模式:
1 function SpecialArray(){
 2     //创建数组
 3     var values = new Array();
 4     //添加值
 5     values.push.apply(values, arguments);
 6     //添加方法
 7     values.toPipedString = function(){
 8         return this.join("|");
 9     };
10     //返回数组
11     return values;
12 }
13 var colors = new SpecialArray("red", "blue", "green");
14 alert(colors.toPipedString());  // "red|blue|green"
返回的对象与构造函数的原型属性之间没有关系,即构造函数返回的对象与在构造函数外部创建的对象没有什么不同。

不能依赖instanceof操作符来确定对象类型。建议在可以使用其他模式的情况下,不要使用这种模式。

7、稳妥构造函数模式

稳妥对象:没有公共属性,其方法也不引用this的对象。稳妥对象最适合在一些安全的环境中(这些环境禁止使用this和new),或者在防止数据被其他应用程序改动时使用。
1 function Person(name, age, job){
 2     //创建要返回的对象
 3     var o = new Object();
 4     //可以在这里定义私有变量和属性
 5 
 6     //添加方法
 7     o.sayName = function(){
 8         alert(name);
 9     };
10     //返回对象
11     return o;
12 }
13 var friend =Person("Anna", 27, "Software Engineer");
14 friend.sayName(); // "Anna"  除了调用sayName()方法外,没有别的方式可以访问其数据成员
15 //  非常适合某些安全执行环境
注:使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此instanceof操作符对这种对象也没有意义。