0. 写在前面
创建对象:
var person = new Object();
person.name = "Nicholas";
person.age = 24;
person.job = "Engineer";
person.sayName = function(){
alert(this.name);
};
如果需要通过接口创建大量对象,那么会产生大量重复代码,工厂模式抽象了创建具体对象的过程。
构造函数:
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName() {
alert(this.name);
}
var person1 = new Person("Nicholas", 24, "Engineer");
var person2 = new Person("Greg", 30, "Doctor");
但构造函数需要在每个实例上都重新创建一遍。全局作用域中定义的函数实际上只能被某个对象调用。
这些问题都可以通过原型模式来解决。
1. 原型模式
事实上每个函数都有其prototype属性,这个属性是一个指向对象的指针,而这个对象就是该类函数所共享的属性和方法。
prototype相当于提供了一个初始值,当给对象实例添加了新的属性,新的属性就会覆盖prototype提供的原始值。如果删掉对象的属性,那么访问到的就是prototype设定的值。不同的对象实例共享一个prototype,减少了冗余度。
function Person(){
}
Person.prototype = {
name : "Nicholas",
age : 24,
job : "Engineer",
sayName : function(){
alert(this.name);
}
};
var p1 = new Person();
p1.name = "Greg";
p1.age = 30;
也就是说,在Person这个原型中,包含constructor, name, age, job, sayName 4个部分。
p1中包含指向原型的指针,还有name, age两个新的属性。访问时候没有新的job和sayName属性,返回时会继续向上搜索,返回原型中的job和sayName属性。
而且由于在原型中查找是一次搜索过程,那么在对象申明之后,给原型中添加新的属性,也是能够成功返回的。
var p2 = new Person();
Person.prototype.sayHi = function(){
alert("Hi");
}
p2.sayHi();
调用pa.sayHi()时,首先会在实例中搜索,没找到的情况下,会继续向上搜索原型。
2. 原型对象的问题
使用原型模式穿件的实例,在默认情况下都取得相同的属性值,在某种情况下会带来不便。
但如果在原型中包含引用类型,问题会更大。
function Person(){
}
Person.prototype = {
name : "Nicholas",
age : 24,
job : "Engineer",
friends : ["Chris", "Flex"],
sayName : function(){
alert(this.name);
}
};
var p1 = new Person();
var p2 = new Person();
p1.friends.push("Lorry");
alert(p1.friends); //"Chris, Flex, Lorry"
alert(p2.friends); //"Chris, Flex, Lorry"
由于friends数组存在prototype中,p1的修改会通过p2反映出来。
所以原型模式很少被单独使用。
3. 组合使用构造函数模式和原型模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
friends : ["Chris", "Flex"];
}
Person.prototype = {
constructor: Person;
sayName : function(){
alert(this.name);
}
};
var p1 = new Person("Nicholas", 24, "Engineer");
var p2 = new Person("Greg", 30, "Doctor");
p1.friends.push("Lorry");
alert(p1.friends); //"Chris, Flex, Lorry"
alert(p2.friends); //"Chris, Flex"
p1修改了p1.friends属性,添加了一个新的字符串,并不影响p2.friends,它们分别引用了不同的数组。
这种混合模式是目前使用最广泛,认同度最高的创建自定义类型的方法。