虽然Object构造函数或对象字面量可以创建单个对象,但他们有个明显缺点:使用同一个接口创建很多对象,会产生大量的重复代码。
1.工厂模式
ECMAScript中没有类,用函数来封装以特定接口创建对象的细节。但没有解决对象识别问题,不能判断对象的类型。
| 1 | function createPerson(name,age,job){ | 
2.构造函数模式
原生构造函数+自定义构造函数
| 1 | 自定义构造函数 | 
- 构造函数必须以大写字母开头
- 要创建新实例,必须使用new操作符,会经历4个步骤- 创建一个新对象
- 新对象原型链接到构造函数原型对象
- 将构造函数的作用域赋给新对象(this绑定到这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
 
- 显式的return表达式将会影响返回结果,但仅限于返回的是一个对象。1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13function Bar() { 
 return 2;
 }
 new Bar(); // 返回新创建的对象
 function Test() {
 this.value = 2;
 return {
 foo: 1
 };
 }
 new Test(); // 返回的对象
2.1将构造函数当作函数
构造函数与其他函数唯一区别,在于调用方式不同,用不用new!
用new就是构造函数
不用就是普通函数
| 1 | //当作构造函数使用 | 
2.2构造函数的问题
- 主要问题:每个方法都要在每个实例上重新创建一遍。从而不同实例上的同名函数是不想等的。 
- 解决方法:把函数定义转移到构造函数外部 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- function Person(name,age,job){ 
 this.name=name;
 this.age=age;
 this.job=job;
 this.sayName=sayName;
 }
 function sayName(){
 alert(this.name);
 }
- 引出新的问题:如果对象定义很多方法,那么就要定义很多个全局函数,于是自定义的引用类型全无封装性了。 
- 新问题解决方法:原型模式 
3.原型模式(原型对象是实例对象的子对象,一个属性)
- 含义:创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含着特定类型的所有实例共享的属性和方法。
- prototype:是原型对象,是通过构造函数而创建的那个对象实例的原型对象。
- 好处:不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。1 
 2
 3
 4
 5
 6
 7
 8
 9function Person(){ 
 
 }
 Person.prototype.name="libowen";
 Person.prototype.age=29;
 Person.prototype.sayName=function(){
 alert(this.name);
 };
3.1理解原型对象
- 原型对象:只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 - prototype属性,这个属性指向函数的原型对象。
- constructor属性:默认情况下,所有原型对象自动获得一个- constructor属性,该属性是一个指针,指向prototype属性所在函数的指针。- constructor属性可以人为修改。
- [[Prototype]]:当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。这个指针(内部属性)是[[Prototype]]。 
- [[Prototype]]是存在于实例与原型对象之间的连接,不是存在于实例和构造函数的连接。  
- isPrototypeOf():来确定原型对象或构造函数与对象实例之间的关系。 - 1 
 2- alert(Person.prototype.isPrototyprOf(person1)); //true 
 alert(Person.prototype.isPrototypeOf(person2)); //true
- Object.getPrototypeOf():ECMAScript5中新增。返回[[prototype]]的值。 - 1 
 2- alert(Object.prototype.getPrototypeOf(person1)==Person.prototype); //true 
 alert(Object.prototype.getPrototypeOf(person2).name); //"libowen"
- 对象实例共享原型所保存的属性和方法的基本原理:先从对象实例本身开始搜索,如果找不到,继续搜索指针指向的原型对象。 
- 屏蔽与屏蔽原因:当为对象实例添加一个属性时,这个属性会屏蔽原型对象中保存的同名属性。原因则是:添加这个属性会阻止我们访问原型中的那个属性,但不会修改那个属性。 
- null与- delete:null只会在实例中设置属性,不能恢复指向原型的链接,delete可以完全删除实例属性,恢复原型链接。- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19- function Person(){ 
 }
 Person.prototype.name="libowen";
 Person.prototype.age=29;
 Person.prototype.sayName=function(){
 alert(this.name);
 };
 var person1=new Person();
 var person2=new Person();
 person1.name="Greg";
 alert(person1.name); //"Greg",来自实例
 alert(person2.name); //"libowen",来自原型
 delete(person1.name);
 alert(person1.name); //"libowen",来自原型
- hasOwnProperty():只在给定属性存在于对象实例中,才返回true。
3.2原型与in操作符
- 两种使用方式:单独使用、for-in循环。- 单独使用时,不区分实例和原型,能够访问到给定属性就返回true。
- 确定属性在原型中的方法
- for-in循环,返回的是所有能够通过对象访问的、可枚举的属性。- 1 
 2
 3- function hasPrototypeProperty(object,name){ 
 return !object.hasOwnProperty(name)&&(name in object);
 }
 
- 单独使用时,不区分实例和原型,能够访问到给定属性就返回
3.3原型的动态性
- 由于在原型中查找值是一次搜索,因此对原型对象所做的任何修改都能够立即从实例上反映出来——即使是先创建实例后修改原型也是如此。
- 如果重写整个原型对象,就切断现有原型(构造函数的prototype属性只指向现有原型)与任何之前已经存在的对象实例之间的联系,对象实例引用的仍然是最初的原型。1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15function Person(){ 
 }
 var friend=new Person();
 Person.prototype={
 constructor:Person,
 name:"Nicholas",
 age:29,
 job:"Software Engineer",
 sayName:function(){
 alert(this.name)
 }
 }
 friend.sayName(); //error
过程如图
3.4原型对象的问题
- 缺点:不能单独使用原型模式的原因。- 省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。
- 所有实例都共享原型的属性,而且原型的属性可以动态更改,导致实例没有属于自己的全部属性。包含基本类型值的属性可以添加屏蔽;包含引用类型值的属性被操作修改后,则会被所有共享反映出来。
 
4.组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性
原型模式用于定义方法和共享的属性
最常用、默认
| 1 | function Person(name,age,job){ | 
5.动态原型模式
可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。
| 1 | //例子 | 
只在sayName()方法不存在的情况下,才会将它添加到原型中。
6.寄生构造函数模式
基本思想:创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。
| 1 | function Person(name, age, job){ | 
- 除了使用 new 操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实
 是一模一样的。
- 通过在构造函数的末尾添加一个return语句且返回的是一个对象,可以重写调用构造函数时返回的值。
- 这个模式可以在特殊的情况下用来为对象创建构造函数。假设我们想创建一个具有额外方法的特殊
 数组。由于不能直接修改Array构造函数,因此可以使用这个模式。1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19function SpecialArray(){ 
 
 //创建数组
 var values = new Array();
 
 //添加值
 values.push.apply(values, arguments);
 
 //添加方法
 values.toPipedString = function(){
 return this.join("|");
 };
 
 //返回数组
 return values;
 }
 
 var colors = new SpecialArray("red", "blue", "green");
 alert(colors.toPipedString()); //"red|blue|green"
注意:
- 首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系,为此,不能依赖instanceof操作符来确定对象类型
- 建议在可以使用其他模式的情况下,不要使用这种模式
7.稳妥构造函数模式
- 稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象
- 用处: 在一些安全的环境中(这些环境中会禁止使用this和new),或者在防止数据被其他应用程序(如 Mashup程序)改动时使用。
- 特点:稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同- 新创建对象的实例方法不引用this;
- 不使用new操作符调用构造函数。
 
- 新创建对象的实例方法不引用
| 1 | function Person(name, age, job){ | 
代码解释:
- 变量 friend中保存的是一个稳妥对象,而除了调用sayName()方法外,没有别的方式可以访问其数据成员。
- 即使有其他代码会给这个对象添加方法或数据成员,但也不可能有别的办法访问传入到构造函数中的原始数据。
