1.工厂模式
一种用函数来封装以特定接口创建对象的方法。1
2
3
4
5
6
7
8
9
10
11
12function 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");
工厂模式虽然解决了创建多个相似对象的问题,但没有解决对象识别问题。
2.构造函数模式
我们可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function 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", 25, "Teacher");
alert (person1.constructor == Person); // true
alert (person2.constructor == Person); // true
alert (person1 instanceof Object); //true
alert (person1 instanceof Person); //true
alert (person1.sayName == person2.sayName); //false
注意:函数名Person的首字母大写,构造函数始终都应该以一个大写字母开头;
要创建Person的新实例,必须使用new操作符。
构造函数与其它函数的唯一区别,就在于通过new操作符来调用。
构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。不同实例上的同名函数是不相等的。
3.原型模式
我们创建的每个函数都有一个prototype(原型)属性。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14function 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
在此,我们将sayName()方法和所有属性直接添加到了Person的prototype属性中,新对象的属性和方法由所有实例共享。
1 | var person1 = new Person(); |
当为实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性。
使用delete操作符则可以删除实例属性。
原型对象的问题,当属性包含引用类型值时,问题就很突出了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18function Person(){}
Person.prototype = {
constructor: Person,
name : "Nicholas",
job : "Software Engineer",
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
实例一般都要有自己的属性,所以原型模式很少用。
4.组合使用构造函数模式和原型模式
组合使用构造函数模式和原型模式,是目前ES中使用最广泛的一种创建自定义类型的方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19function 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();
var person2 = new Person();
person1.friends.push("Van");
alert(person1.friends); // "Shelby,Court,Van"
alert(person2.friends); // "Shelby,Court"
alert(person1.friends == person2.friends); //false
alert(person1.sayName == person2.sayName); //true
在这个例子中,实例属性都在构造函数中定义,而由所有实例共享的属性constructor和方法sayName()则是在原型中定义的。这样每个实例都会有自己的一份实例属性的副本,而又共享着对方法的引用。
5.动态原型模式
为了解决构造函数和原型分离的问题,可以在构造函数中初始化原型。1
2
3
4
5
6
7
8
9
10function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
if (typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
if语句检查的是初始化之后应该存在的任何属性和方法,只要检查其中一个即可。对于采用这种模式创建的对象,还可以使用instanceof操作符确定它的类型。这种方法比较完美了。
6.寄生构造函数模式
除了使用new操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实一模一样。通常在前述几种模式都不适用的情况下可以使用寄生构造函数模式。比如我们想创建一个具有额外方法的特殊数组,由于不能直接修改Array构造函数,因此使用这个模式。1
2
3
4
5
6
7
8
9
10
11function 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.稳妥构造函数模式
Douglas Crockford发明了JavaScript中的稳妥对象这个概念。所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象。稳妥对象最适合在一些安全的环境中或者防止数据被其它应用程序改动时使用。1
2
3
4
5
6
7
8
9function Person(name, age, job){
var o = new Object();
o.sayName = function(){
alert(name);
};
return o;
}
var friend = Person("Nicholas", 29, "Software Engineer");
friend.sayName(); // "Nicholas"
注意,以这种模式创建的对象中,除了使用sayName()方法外,没有别的办法访问name的值。
参考书籍:《JavaScript高级程序设计》,作者:【美】 Nicholas C.Zakas