一、工厂模式
function createPerson(name,age,job){
var o=new Object();
o.name=name;
o.age=age;
o.job=job;
o.sayName=function(){
alert(this.name);
};
return o;
}
var person1=createPerson('Nicholas',29,'Software Engineer');
var person2=createPerson('Greg',27,'Doctor');
工厂模式虽然解决了创建多个对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
二、构造函数模式
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
};
}
var person1=new Person('Nicholas',29,'Software Engineer');
var person2=new Person('Greg',27,'Doctor');
与工厂模式相比:
1.没有显示地创建对象
2.直接将属性和方法赋给了this对象
3.没有return语句
4.能用instanceof 标识对象类型
构造函数的问题:每个方法都要在每个实例上重新创建一遍。如上例,person1和person2都有一个名为sayName()的方法,但那两个方法不是同一个Function实例。上例中sayName的创建可以看成:this.sayName=new Function('alert(this.name)');因此person1.sayName!=person2.sayName()
三、原型模式
使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
function Person(){}
Person.prototype.name='Nicholas';
Person.prototype.age=29;
Person.prototype.job='Software Engineer';
Person.prototype.sayName=function(){alert(this.name);};
var person1=new Person();
person1.sayName();//Nicholas
var person2=new Person();
person2.sayName();//Nicholas
alert(person1.sayName==person2.sayname);//true
更简单的原型语法:
function Person(){}
Person.prototype={
name:'Nicholas';
age:29;
job:'softWare';
sayName:function(){alert(this.name);}
}
每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性。而我们在这里使用的语法,本质上完全重写了默认的prototype对象,因此constructor属性就变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数。可以向下面这样特意设置回适当的值:
Person.prototype={
constructor:Person;
name:'Nicholas';
age:29;
job:'softWare';
sayName:function(){alert(this.name);}
}
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。
原型的动态性:
由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来,即使是先创建了实例后修改原型也照样如此。尽管可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但如果是重写整个原型对象,那么情况就不一样。重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系。
原型对象的问题:
1.省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。
2.最大的问题是由其共享的本性所导致的。原型中所有属性是被很多实例共享的,对于那些包含基本值得属性倒也说得过去,可以通过在实例上添加一个同名属性,可以隐藏原型中的对应属性。然而,对于包含引用类型值得属性来说,问题就比较突出。
function Person(){}
Person.prototype={
constructor:Person;
name:'Nicholas';
age:29;
job:'softWare';
friends:['shelby','court'];
sayName:function(){alert(this.name);}
}
var person1=new Person();
var person2=new Person();
person1.friends.push('van');
alert(person1.friends)//shelby,court,van
alert(person2.friends)//shelby,court,van
alert(person1.friends==person2.friends)//true;
四、组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=['shelby','court'];
}
Person.prototype={
constructor:Person;
sayName:function(){
alert(this.name);
}
}
var person1=new Person('Nic',29,'soft');
var person2=new Person('greg',27,'doc');
person1.friends.push('van');
alert(person1.friends)//shelby,count,van
alert(person2.friends)//shelby,count
alert(person1.friends==person2.friends)//false
alert(person1.sayName==person2.sayName)//true;
五、动态原型模式
把所有信息都封装在构造函数中,通过在构造函数中初始化原型(仅在必要情况下),又保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=['shelby','court'];
if(typeof this.sayName!='function'){
Person.prototype.sayName=function(){alert(this.name)};
}
}
var friend=new Person('Nic',29,'soft');
frind.sayName();
if语句检查的可以是初始化之后应该存在的任何属性或方法,不必用一大堆if语句检查每个属性和每个方法;只要检查其中一个即可。
六、寄生构造函数模式
创建一个函数,该函数的作用仅仅是封装对象的代码,然后再返回新创建的对象;但从表面上看,这个函数又很像是典型的构造函数。
除了使用new操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式起始是一模一样的。
function 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操作符来确定对象类型。
七、稳妥构造函数模式
稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用this和new),或者在防止数据被其他应用程序改动时使用。稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:新创建对象的实例方法不引用this;不使用new操作符调用构造函数。
function Person(name,age,job){
var o=new Object();
o.name=name;
o.age=age;
o.job=job;
o.friends=['shelby','court'];
o.sayName=function(){alert(name);}
return o;
}
var friend=Person('Nic',29,'soft');
friend.sayName()//Nic
friend保存了一个稳妥对象,除了调用sayName()方法,没有别的方法可以访问其数据成员。即使有其他代码会给这个对象添加方法或数据成员,但也不可能有别的方法访问传入到构造函数中的原始数据。
instanceof操作符对这种对象没有作用。
八、总结
工厂模式:使用简单的函数创建对象,为对象添加属性和方法,然后返回对象。
构造函数模式:创建自定义引用类型,可以像创建内置对象实例一样使用new操作符。不过他的每个成员都无法得到复用,包括函数。
原型模式:使用构造函数的prototype属性来指定那些应该共享的方法属性。组合使用构造函数模式和原型模式时,使用构造函数定义实例属性,使用原型定义共享属性和方法。