在《JavaScript高级程序设计》中,介绍了创建对象的7种方式,分别是:工厂模式、构造函数模式、原型模式、构造函数和原型组合模式、动态原型模式、寄生构造函数模式以及稳妥构造函数模式。
下面分别具体介绍下这几种模式。
一、工厂模式
通过函数来封装以特定接口创建对象的细节。
案例:
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
}
return o;
}
var p1 = new createPerson("xiaoLe",23,"Engineer");
var p2 = new createPerson("xiaoDing",31,"Doctor");
这种方式的好处在于可以快速创建相似的对象,问题在于无法区分对象类型[如p1和p2都是Object类型]。于是出现了构造函数模式。
二、构造函数模式
案例:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
}
}
var p1 = new Person("xiaoLe",23,"Engineer");
var p2 = new Person("xiaoDing",31,"Doctor");
//构造函数模式产生的问题
console.log(p1.sayName() == p2.sayName());//false
构造函数模式与工厂模式的区别是,没有显式地创建对象、属性和方法直接赋给this对象、没有return语句;在使用构造函数创建对象时,必须使用new操作符。
通过构造函数模式创建的实例,可以通过instanceof操作符来判断其类型。但不足是,若构造函数中有方法,那么每个方法都会在实例上重新创建一遍,而这是没有必要的。但如果将构造函数中的方法挪到构造函数外,那这个方法又变成全局函数。为了解决这个问题,从而出现了原型模式。
三、原型模式
案例:
function Person(){
}
Person.prototype = {
constructor:Person,
name:"xiaoMi",
age : 30,
job : "Engineer",
sayName = function(){
console.log(this.name);
}
};
var p1 = new Person();
p1.sayName();//"xiaoMi"
var p2 = new Person();
p2.sayName();//"xiaoMi"
console.log(p1.sayName() == p2.sayName());//true
//原型模式的问题
Person.prototype.friends = ["huawei","vivo"]
var p3 = new Person();
var p4 = new Person();
p3.friends.push("oppo");
console.log(p3.friends);//"huawei","vivo","oppo"
console.log(p3.friends === p4.friends);//true
原型模式实现了属性和方法被实例共享,实例可以通过重新属性实现属性值个性化,但是这只有在属性类型为基本类型时才有效,如果属性类型为引用类型,就会出现上面friends属性的问题。为了解决这个问题,出现了构造函数和原型组合模式。
四、构造函数和原型组合模式
案例:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["huawei","vivo"];
}
Person.prototype = {
constructor:Person,
sayName:function(){
console.log(this.name);
}
}
var p1 = new Person("xiaoMi",23,"Engineer");
var p2 = new Person("xiaoLe",23,"Engineer");
p1.friends.push("oppo");
console.log(p1.friends);//"huawei","vivo","oppo"
console.log(p2.friends);//"huawei","vivo"
这种模式顾名思义就是组合使用构造函数模式和原型模式,其中,构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。这也是目前创建自定义类型的最常见方式.
五、动态原型模式
案例:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
if( typeof this.sayName != "function"){
Person.prototype.sayName = function(){
console.log(this.name);
}
}
}
var p1 = new Person("xiaoLe",23,"Engineer");
使用构造函数和原型组合模式时,往往是将构造函数和原型分开写,容易让人产生困惑,于是出现了这种模式,将所有信息都封装在构造函数中,通过在构造函数中初始化原型。
六、寄生构造函数模式
案例:
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");
console.log(colors.toPipedString());//"red|blue|green"
这种模式可以在特殊情况下[比如对一些引用类型增加额外方法,同时又不想修改该引用类型的原型]用来为对象创建构造函数。
七、稳妥构造函数模式
案例:
function Person(name,age,job){
var o = new Object();
o.sayName = function(){
console.log(name);
}
return o;
}
var p1 = Person("xiaoLe",23,"Engineer");
p1.sayName();//xiaoLe
该模式适合在某些安全执行环境。