Javascript创建对象的七种方法总结

   最近再次拿起《Javascript高级程序设计(第三版)》,真的感觉经典的书何时读都会很有收获,相信好多人都推荐过这本书,这次也是给大家分享下其中创建对象这个主题,也是让自己更加深刻一下。

一、工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
例: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("jim",29,"teacher");
var person2 = createPerson("tom",29,"doctor");
工厂模式虽然解决了创建多个对象的问题,但却没有解决对象识别的问题(即怎么知道一个对象的类型);

二、构造函数模式

1
2
3
4
5
6
7
8
9
10
例:function Person(name,age,job) {
this.name = name;
this.age = age;
this .job = job;
this .sayName = function ( ) {
alert(this.name);
};
}
var person1 = new Person("jim",29,"teacher");
var person2 = new Person("tom",29,"doctor");

特点:

①、没有显示地创建对象;
②、直接将属性和方法赋给了this对象;
③、没有return语句
创建Person的新实例,必须通过new操作符,以这种方式调用构造函数会经历以下四个步骤:
①、创建一个新对象;
②、将构造函数的作用域赋给新对象(因此this就指向了这个新对象);
③、执行构造函数中的代码(初始化对象);
④、返回新对象
这样就能够通过instanceof检测实例了,对象也就具备了类型

三、原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person( ) {
}
Person.prototype.name = "jim";
Person.prototype.age = 29;
Person.prototype.job = teacher";
Person.prototype.sayName = function ( ) {
alert(this.name);
};
var person1 = new Person( );
person1.sayName( );
var person2 = new Person( );
person2.sayName( );
alert(person1.sayName === person2.sayName) //true

缺点:原型模式的最大问题是由其共享的本性所导致的,这种共享对于函数非常合适,对于那些包含基本值的属性倒也还说得过去,通过在实例中添加一个同名属性,可以隐藏原型中的属性,然后对于引用类型值的属性来说,问题就比较突出了,只要一个实例改变,则所有是例都会发生同样的改变,因为实例只是有一个指针指向这个共享的引用类型,所以在开发中经常采用下面的这种组合模式;

四、组合使用构造函数模式和原型模式

   构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时共享着对方法的引用,最大限度地节省了内存,另外,这种模式还支持向构造函数传参,可以说是集两种模式之长;

五、动态原型模式

   它把所有的信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点,换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型;
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function 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);
};
}
}
var person1 = new Person("jim",29,"teacher");
person1.sayName( );

这里只会在sayName( )方法不存在的情况下,才会将它添加到原型中,其中,if语句检查的可以是初始化之后应该存在的任何属性和方法-不必用一大堆if语句检查每个属性和每个方法,只要检查其中一个即可,对于采用这种模式创建的对象,还可以使用instanceof操作符确定它的类型;
注意:使用这种动态原型模式时,不能使用对象字面量重写原型,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系;

六、寄生构造函数模式

   这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的象,从表面上看,这个函数很像典型的构造函数;

例:

1
2
3
4
5
6
7
8
9
10
11
function Person(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 friend = new Person("jim",29,"teacher");

   在这个例子中,除了使用new操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实是一模一样的。构造函数在不返回值的情况下,默认会返回新对象实例,而通过在构造函数末尾添加一个return语句,可以重写调用构造函数时返回的值,如果是简单数据类型直接忽略,是引用类型的话是直接返回这个对象。
   这个模式可以在特殊情况下用来为对象构造函数,注意一下接下来的说明:返回的对象和构造函数或与构造函数的原型属性之间没有关系,也可以说,构造函数返回的对象与构造函数外部创建的对象没有什么不同,也就不能一栏instanceof操作符来确定对象类型了。只是应用一些很少的特殊情况(可以起到保护内部数据的作用)
假设我们想创建一个具有额外方法的特殊数组,由于不能直接修改Array构造函数,因此可以使用这个模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
//这样就能确保这个方法只应用这个特殊数组,而不是所有数组了

七、稳妥构造函数模式

   道格拉斯.克罗克福德发明了Javascript中的稳妥对象这个概念,指的是没有公共属性,而且其方法也不引用this的对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止this和new)或者防止被其他应用程序(如Mashup程序)改动时使用。稳妥构造函数与寄生构造函数模式类似,但有两点不同:一是新创建对象的实例方法不引用this;二是不使用new操作符调用构造函数;

例:

1
2
3
4
5
6
7
8
9
10
11
 function Person(name,age,job) {
//创建要返回的对象
var o = new Object( );
o.sayName = function( ) {
alert(name);
};
return o;
}

var friend = Person("jim",29,"teacher");
friend.sayName( ); //jim

   这样变量friend中保存的是一个稳妥对象,而除了调用sayName()方法以外,没有别的方式可以访问其数据成员,即使有其他代码会给这个对象添加方法或数据成员,但也不可能有别的方法访问传到构造函数中的原始数据。这种模式提供的这种安全性,使得它非常适合在某些安全执行环境提供的环境下使用,与寄生构造函数模式类似,使用稳妥构造函数创建的对象与构造函数之间也没有关系,因此instanceof操作符对这种对象也没有意义,其实我现在理解主要也是利用了闭包的思想,用函数包起来之后去保护一些私有变量;