javascript设计模式理论和示例深入分析(下)

6.2.4 组合使用构造函数模式和原型模式(解决原型模式中引用类型值不能的实例不能私有化问题)

创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。下面的代码重写了前面的例子。

function 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("Nicholas", 29, "Software Engineer");

var person2 = new Person("Greg", 27, "Doctor");

person1.friends.push("Van");

alert(person1.friends); //"Shelby,Count,Van"

alert(person2.friends); //"Shelby,Count"

alert(person1.friends === person2.friends); //false

alert(person1.sayName === person2.sayName); //true

在这个例子中,实例属性都是在构造函数中定义的,而由所有实例共享的属性 constructor 和方法 sayName() 则是在原型中定义的。而修改了 person1.friends (向其中添加一个新字符串) ,并不会影响到 person2.friends ,因为它们分别引用了不同的数组。这种构造函数与原型混成的模式,是目前在 ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式。

6.2.5 动态原型模式

有其他 OO 语言经验的开发人员在看到独立的构造函数和原型时,很可能会感到非常困惑。动态原型模式正是致力于解决这个问题的一个方案,它把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下) ,又保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。来看一个例子。

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 friend = new Person("Nicholas", 29, "Software Engineer");

friend.sayName();

注意构造函数代码中加粗的部分。这里只在 sayName() 方法不存在的情况下,才会将它添加到原型中。这段代码只会在初次调用构造函数时才会执行。此后,原型已经完成初始化,不需要再做什么修改了。不过要记住,这里对原型所做的修改,能够立即在所有实例中得到反映。因此,这种方法确实可以说非常完美。其中, if 语句检查的可以是初始化之后应该存在的任何属性或方法——不必用一大堆if 语句检查每个属性和每个方法;只要检查其中一个即可。对于采用这种模式创建的对象,还可以使用 instanceof
操作符确定它的类型。使用动态原型模式时,不能使用对象字面量重写原型。前面已经解释过了,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。

6.2.6 寄生构造函数模式

通常,在前述的几种模式都不适用的情况下,可以使用寄生(parasitic)构造函数模式。这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象;但从表面上看,这个函数又很像是典型的构造函数。下面是一个例子。

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("Nicholas", 29, "Software Engineer");

friend.sayName(); //"Nicholas"

在这个例子中, Person 函数创建了一个新对象,并以相应的属性和方法初始化该对象,然后又返回了这个对象。 除了使用 new 操作符并把使用的包装函数叫做构造函数之外, 这个模式跟工厂模式其实是一模一样的。构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数的末尾添加一个 return 语句,可以重写调用构造函数时返回的值。这个模式可以在特殊的情况下用来为对象创建构造函数。 假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改 Array 构造函数,因此可以使用这个模式。

function SpecialArray(){

//创建数组

var values = new Array();

//添加值

values.push.apply(values, arguments);  //call 以参数表来接受被调用函数的参数

//添加方法

values.toPipedString = function(){

return this.join("|");

};

//返回数组

return values;

}

var colors = new SpecialArray("red", "blue", "green");

alert(colors.toPipedString()); //"red|blue|green"

在这个例子中,我们创建了一个名叫 SpecialArray 的构造函数。在这个函数内部,首先创建了一个数组,然后 push() 方法(用构造函数接收到的所有参数)初始化了数组的值。随后,又给数组实例添加了一个 toPipedString() 方法,该方法返回以竖线分割的数组值。最后,将数组以函数值的形式返回。接着,我们调用了 SpecialArray 构造函数,向其中传入了用于初始化数组的值,此后又调用了 toPipedString() 方法。关于寄生构造函数模式,有一点需要说明:首先,返回的对象与构造函数或者与构造函数的原型属

性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,不能依赖 instanceof 操作符来确定对象类型。 由于存在上述问题, 我们建议在可以使用其他模式的情况下,不要使用这种模式。

6.2.7 稳妥构造函数模式

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

的 Person 构造函数重写如下。

function Person(name, age, job){

//创建要返回的对象

var o = new Object();

//可以在这里定义私有变量和函数

//添加方法

o.sayName = function(){

alert(name);

};

//返回对象

return o;

}

注意, 在以这种模式创建的对象中, 除了使用 sayName() 方法之外, 没有其他办法访问 name 的值。可以像下面使用稳妥的 Person 构造函数。

var friend = Person("Nicholas", 29, "Software Engineer");

friend.sayName(); //"Nicholas"

这样,变量 friend 中保存的是一个稳妥对象,而除了调用 sayName() 方法外,没有别的方式可以访问其数据成员。即使有其他代码会给这个对象添加方法或数据成员,但也不可能有别的办法访问传入到构造函数中的原始数据。稳妥构造函数模式提供的这种安全性,使得它非常适合在某些安全执行环境——例如,ADsafe(www.adsafe.org)和 Caja(http://code.google.com/p/google-caja/)提供的环境——下使用。

时间: 2024-10-11 10:48:37

javascript设计模式理论和示例深入分析(下)的相关文章

javascript设计模式理论和示例深入分析(上)

此文详细剖析的设计模式理论,特别是原型设计模式,帮助在遇到实际项目中提供理论指导和分析. 虽然 Object 构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码.为解决这个问题,人们开始使用工厂模式的一种变体. 6.2.1 工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程 , 考虑到在 ECMAScript 中无法创建类,开发人员 就发明了一种函数,用函数来封装以特定接口创建对象的细节,如

Javascript设计模式理论与实战:工厂方法模式

本文从简单工厂模式的缺点说起,引入工厂方法模式,介绍的工厂方法模式的基本知识,实现要点和应用场景,最后举例进行说明工厂方法模式的应用.在之前的<Javascript设计模式理论与实战:简单工厂模式>这篇文章中,我们介绍了简单工厂的知识和一些应用.简单工厂模式存在一个唯一的工厂类,它的优点是所有产品类的实例化集中管理,便于理解,但这既是优点也是缺点.如果产品类的数量较少并且不会经常发生变化,我们可以直接利用简单工厂模式,但是有的时候,需求是随时在变的,产品类也可能随时在增加,如果使用简单工厂模式

Javascript设计模式理论与实战:适配器模式

有的时候在开发过程中,我们会发现,客户端需要的接口和提供的接口发生不兼容的问题.由于特殊的原因我们无法修改客户端接口.在这种情况下,我们需要适配现有接口和不兼容的类,这就要提到适配器模式.通过适配器,我们可以在不用修改旧代码的情况下也能使用它们,这就是适配器的能力. 基本理论 适配器模式:将一个接口转换成客户端需要的接口而不需要去修改客户端代码,使得不兼容的代码可以一起工作. 适配器主要有3个角色组成:(1)客户端:调用接口的类(2)适配器:用来连接客户端接口和提供服务的接口的类(3)适配者:提

Javascript设计模式理论与实战:桥接模式

桥接模式将抽象部分与实现部分分离开来,使两者都可以独立的变化,并且可以一起和谐地工作.抽象部分和实现部分都可以独立的变化而不会互相影响,降低了代码的耦合性,提高了代码的扩展性. 基本理论 桥接模式定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化.桥接模式主要有4个角色组成:(1)抽象类(2)扩充抽象类(3)实现类接口(4)具体实现类根据javascript语言的特点,我们将其简化成2个角色:(1)扩充抽象类(2)具体实现类怎么去理解桥接模式呢?我们接下来举例说明 桥接模式的实现 理解桥

Javascript设计模式理论与实战:简单工厂模式

通常我们创建对象最常规的方法就是使用new关键字调用构造函数,这会导致对象之间的依赖性.工厂模式是一种有助于消除类之间依赖性的设计模式,它使用一个方法来决定要实例化哪一个类.本文详细介绍了简单工厂模式的理论,并且举例说明了简单工厂模式的具体应用. 基本介绍 简单工厂模式是工厂模式中最基本的一种.通过定义一个工厂类,根据参数实例化具体的某个产品类. 举例说明 我们举个例子进行说明:假设我们开发一个旅游行业网站,网站上面销售机票,酒店等产品.一个用户准备购买一张机票.我们可以定义相关类如下: 1 v

Javascript设计模式理论与实战:组合模式

我们平时开发过程中,一定会遇到这种情况:同时处理简单对象和由简单对象组成的复杂对象,这些简单对象和复杂对象会组合成树形结构,在客户端对其处理的时候要保持一致性.比如电商网站中的产品订单,每一张产品订单可能有多个子订单组合,比如操作系统的文件夹,每个文件夹有多个子文件夹或文件,我们作为用户对其进行复制,删除等操作时,不管是文件夹还是文件,对我们操作者来说是一样的.在这种场景下,就非常适合使用组合模式来实现. 基本知识 组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对

模式(一)javascript设计模式

模式有三种:Architectural Pattern.Design Pattern.Coding Pattern,即:框架模式.设计模式.编程模式.本文主要讲解javascript中的设计模式,好的设计模式能够提高代码的重用性,可读性,使代码更容易的维护和扩展.本文适合有一点javascript基础,对javascript的概念有所了解. 一.单例模式: 单例模式是javascript中最常用的模式,它是将自己的代码放在一个命名空间下,这样的好处是可以减少使用全局变量,在多人协同开发时也能避免

21种JavaScript设计模式最新记录(含图和示例)

最近观看了<Javascript设计模式系统讲解与应用>教程,对设计模式有了新的认识,特在此做些记录. 一.UML 文中会涉及众多的UML类图,在开篇需要做点基础概念的认识.以下面的图为例,图片和说明均来源于<大话设计模式>一书. (1)矩形框,它代表一个类.类图分三层,第一层显示类的名称,如果是抽象类,则用斜体显示.第二层是类的特性,通常就是字段和属性.第三层是类的操作,通常是方法或行为.前面的符号,+ 表示public,- 表示private,# 表示protected. (2

JavaScript设计模式 - 迭代器模式

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素 许多浏览器都支持 Javascript 的 Array.prototype.forEach 迭代器可以分为 内部迭代器 和 外部迭代器 一.jQuery 中的迭代器 1 $.each( [1,2,3,4], function (i, n) { 2 console.log( "当