javaScript是一种基于对象和事件驱动并具有相对安全性的客户端脚本语言。同时也是一种广泛用于客户端Web开发的脚本语言,常用来给html网页添加动态功能,比如响应用户的各种操作。Javascript也可以用于其他场合,如服务器端编程。
本文介绍的是javascript创建对象的7种模式:工厂模式、构造函数模式、原型模式、动态原型模式、组合使用构造函数模式和原型模式、寄生构造函数模式、稳妥构造函数模式。
1.工厂模式
工厂模式,是专门负责将大量有共同接口的类实例化,而且不必事先知道每次是要实例化哪一个类的模式。它定义一个用于创建对象的接口,由子类决定实例化哪一个类。工厂模式相当于创建实例对象的new,经常要根据类Class生成实例对象,如Aa=newA()。工厂模式也是用来创建实例对象的,工厂模式是现今最常用的模式,在java程序系统中随处可见。
这种模式是创建具体对象的抽象过程。考虑到在ECMA Script中无法创建类,于是开发人员发明了一种函数,用函数来封装以特定接口创建对象的细节。
函数create Person()能够根据接受的参数来构建一个包含所有必要信息的Person对象。
可以无数次地调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。
工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
随着JavaScript的发展,又一个新模式出现了。
2.构造函数模式
ECMA Script中的构造函数可用来创建特定类型的对象,例如Object和Array这样的原生构造函数。
在这个例子中,Person()函数取代了createPerson()函数。
我们注意到,Person()中的代码除了与createPerson()中相同的部分外,还存在以下不同之处:
a)没有显式地创建对象;
b)直接将属性和方法赋给了this对象;
c)没有return语句。
要创建Person的新实例,必须使用new操作符。以这种方式调用构造函数实际上会经历以下4个步骤:
(1)创建一个新对象;
(2)将构造函数的作用域赋给新对象(因此this就指向了这个新对象);
(3)执行构造函数中的代码(为这个新对象添加属性);
(4)返回新对象。
在前面例子的最后,person1和person2分别保存着Person的一个不同的实例。这两个对象都有一个constructor(构造函数)属性,该属性指向Person,如下所示。
但是,提到检测对象类型,还是instan-ceof操作符要更可靠一些。我们在这个例子中创建的所有对象既是Object的实例,同时也是Person的实例,这一点通过instanceof操作符可以得到验证。
构造函数的问题
使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。
person1和person2都有一个名为sayName()的方法,但那两个方法不是同一个Function的实例。
以下代码可以证明这一点。
因此,可以像下图所示的一样,通过把函数定义转移到构造函数外部来解决这个问题。
3.原型模式
Prototype原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另一个可定制的对象,无需知道如何创建的细节。工作原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
创建每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
如果按照字面意思来理解,那么prototype就是通过调用构造函数而创建的那个对象实例的原型对象。
使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。
即不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
#p#分页标题#e#
但与构造函数模式不同的是,新对象的这些属性和方法是由所有实例共享的。也就是说,person1和person2访问的都是同一组属性和同一个say Name()函数。
只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
就拿前面的例子来说,Person.prototype.constructor指向Person。而通过这个构造函数,我们还可继续为原型对象添加其他属性和方法。
当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。
ECMA-262第5版中管这个指针叫[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]],但Firefox、Safari和Chrome在每个对象上都支持一个属性__proto__;而在其他实现中,这个属性对脚本则是完全不可见的。
不过,要明确的真正重要的一点就是,这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。
此外,要格外注意的是,虽然这两个实例都不包含属性和方法,但可以调用person1.say Name()。
这是通过查找对象属性的过程来实现的。(属性->原型->原型…)
4.组合使用构造函数模式和原型模式
创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。
另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。
是目前在ECMA Script中使用最广泛、最受认同的一种创建自定义类型的方法。
5.动态原型模式
把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。这是动态原型模式一直致力于解决的问题。也就是说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。
注意构造函数代码中加粗的部分。只有当say Name()方法不存在时,才会将它添加到原型中。
使用动态原型模式时,不能使用对象字面量重写原型。前文也有解释,若已经创建了实例,再重写原型,那么就会切断现有实例与新原型之间的联系。
6.寄生构造函数模式
若上述的几种模式都不适用,则可以使用寄生(parasitic)构造函数模式。这种模式的基本思想是:创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象;但从表面上看,这个函数又很像是典型的构造函数。
除了使用new操作符并把使用的包装函数叫做构造函数之外,其实这个寄生构造函数模式跟工厂模式是相同的。
工厂模式为:
关于寄生构造函数模式,需要说明一点:首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系;即构造函数返回的对象与在构造函数外部创建的对象并没有不同。为此,不能依赖instanceof操作符来确定对象类型。由于存在上述问题,建议在可以使用其他模式的情况下,就不要使用寄生构造函数模式。
7.稳妥构造函数模式
稳妥对象没有公共属性,其方法不引用this的对象。
稳妥对象最适合在一些安全的环境中(这些环境中禁止使用this和new),或在防止数据被其他应用程序(如Mashup程序)改动时使用。
稳妥构造函数遵循与寄生构造函数类似的模式,但以下这两点不同:
(1)新创建对象的实例方法不引用this;
(2)不使用new操作符调用构造函数。按照稳妥构造函数的要求,可以将前面的Person构造函数重写如下:
注意:在以这种模式创建的对象中,除了使用say Name()方法之外,没有其他办法访问name的值。
与寄生构造函数模式类似,使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此instanceof操作符对这种对象也没有意义。
#p#分页标题#e#
上述介绍的便是javascript创建对象常见的7种模式,在学习javascript的过程中,学习它的面向对象编程是从javascript入门初级向中级的过渡。因此,掌握javascript创建对象的模式十分重要。