前几天,有总结过javascript中对象的一些知识——【理解javascript中的对象属性】和【javascript原型对象、构造函数和实例对象】,今天在这个基础上,学习了javascript中,创建对象的几种方法。
其实,在【javascript引用类型之object类型】中有提到过两种。用字面量跟构造函数。今天所说的方法,有封装的概念,将属性和方法封装成一个对象,再由此创建实例。
工厂模式
所谓的工厂模式,就是利用函数来定义一个创建对象的接口。然后每次都利用这个函数来创建对象。
function createPerson(name,age){
var o=new Object();
o.name=name;
o.age=age;
return o;
}
var p1=createPerson("Tom",24);
alert(p1.name); //Tom
alert(p1.age); //24
在这个例子中,createPerson函数内部,用Object类创建了一个对象,该对象的属性值由函数参数获得,最后返回这个对象。接下来实例化的过程就是调用这个函数来实现的。
这种创建对象的方法能够满足创建多个相似对象的需求,但是,不能知道一个对象是属于什么类型(这里继续沿用前面的规则,对js引入一个不规范的类的概念。)
构造函数模式
javascript中可以由构造函数创建特定类型的对象。如Array、Date等。这边的Array、Date就是javascript中原生的构造函数。我们也可以自定义构造函数,由此来创建自定义类型的对象。利用构造函数来创建对象,就是构造函数模式。
function Person(name,age){
this.name=name;
this.age=age;
this.sayName=function(){
return this.name;
}
}
var p1=new Person("Tom",24);
alert(p1.name); //Tom
alert(p1.age); //24
alert(p1.sayName()); //Tom
注意,使用构造函数来创建实例,必须使用new操作符。
构造函数的优点是,它可以将它的实例标识为特定的类型。
在【javascript原型对象、构造函数和实例对象】中有提过,使用构造函数实例化的属性是实例属性,即该构造函数的每个实例中都拥有一份属性的副本。对于实现同样功能的方法来说,这实在是没有必要,而且造成内存的浪费。
原型模式
所谓的原型模式也就是通过原型来定义属性和方法。换句话说,不必在构造函数中定义对象的信息,而全部将这些信息添加到对象的原型中。
function Person(){}
Person.prototype.name="Tom";
Person.prototype.age="24";
Person.prototype.friends=["Sam","Lily"];
var p1=new Person();
var p2=new Person();
alert(p1.friends); //Sam,Lily
alert(p2.friends); //Sam,Lily
p1.friends.push("Bob");
alert(p1.friends); //Sam,Lily,Bob
alert(p2.friends); //Sam,Lily,Bob
这个例子就是利用原型模式来创建Person类。所有属性都有原型来定义。实例化了两个实例分别为p1和p2。在代码中,我们为p1添加了一个新的朋友,但是p2也一样增加了这个朋友。这其实并不是我们想要的。为什么会这样呢?在【
javascript原型对象、构造函数和实例对象】中也有说到,在原型对象中定义的属性和方法是所有对象实例所共享的,其中一个修改了值,会影响到其他实例。这就是单独使用原型模式的弊端。
但是有点需要注意的,假如是实例重写了属性,那则不会修改原型中的值,并且重写的值会覆盖掉原型中的定义的值:
function Person(){}
Person.prototype.name="Tom";
Person.prototype.age="24";
Person.prototype.friends=["Sam","Lily"];
var p1=new Person();
var p2=new Person();
p1.friends=["Bob"];
alert(p1.friends); //Bob
alert(p2.friends); //Sam,Lily
组合使用构造函数和原型模式
看了前面构造函数模式和原型模式的优缺点,那么其实就可以综合二者的优点来创建对象,这也是创建自定义类型时,最常见的方法。
function Person(name,age){
this.name=name;
this.age=age;
this.friends=["Sam","Bob"];
}
Person.prototype={
constructor:Person,
sayName:function(){
return this.name;
}
};
var p1=new Person("Lily",29);
var p2=new Person("Tom",24);
alert(p1.age); //29
alert(p2.age); //24
p1.friends.push("Susan");
alert(p1.friends); //Sam,Bob,Susan
alert(p2.friends); //Sam,Bob
alert(p1.sayName()); //Lily
alert(p2.sayName()); //Tom
动态原型模式
所谓动态原型模式就是指,将对象信息都封装在构造函数中,但是在构造函数内部初始化原型(仅在需要的情况下)。
function Person(name,age){
this.name=name;
this.age=age;
if(typeof this.sayName!="function"){
Person.prototype.sayName=function(){
return this.name;
}
}
}
var p1=new Person("Tom",29);
var p2=new Person("Lily",24);
alert(p1.age); //29
alert(p2.age); //24
alert(p1.sayName()); //Tom
alert(p2.sayName()); //Lily
这个例子,只在sayName()方法不存在的情况下,才会添加到原型中。即,只在初次调用构造函数时才会执行。在这之后,原型已经完成初始化,就不需要再做修改了。
这种方法也同样结合了构造函数模式和原型模式的有点。并且,结构更为简洁些。
注意:使用动态原型模式时,不能使用对象字面量重写原型。
以上就是javascript中创建对象中比较常用的方法。并没有说哪个方法绝对的好,或者绝对的不好。主要是根据实际情况,选择最适合的方式,才是最好的。