@by Ruth92(转载请注明出处)

第5章:对象创建模式

JavaScript 是一种简洁明了的语言,并没有其他语言中经常使用的一些特殊语法特征,如 命名空间模块私有属性 以及 静态成员 等语法。

但是通过常见的模式,可以实现、替换其他语言中的语法特征。

1. 命名空间模式

// 全局变量,不安全
var MYAPP = {}; // 更好的代码风格
if (typeof MYAPP === 'undefined') {
var MYAPP = {};
} // 或者用更短的语句
var MYAPP = MYAPP || {}; // 构造函数
MYAPP.Parent = function() {};
MYAPP.Child = function() {}; // 一个变量
MYAPP.some_var = 1; // 一个对象容器
MYAPP.modules = {}; // 嵌套对象
MYAPP.modules.module1 = {};
MYAPP.modules.module1.data = {a: 1, b: 2};
MYAPP.modules.module2 = {};

【通用命名空间函数实现示例】:

var MYAPP = MYAPP || {};

MYAPP.namespace = function(ns_string) {
var parts = ns_string.split('.'),
parent = MYAPP,
i; // 剥离最前面的冗余全局变量
if (parts[0] === 'MYAPP') {
parts = parts.slice(1);
} for (i = 0; i < parts.length; i++) {
// 如果它不存在,就创建一个属性
if (typeof parent[parts[i]] === 'undefined') {
parent[parts[i]] = {};
}
parent = parent[parts[i]];
} return parent;
} // 测试:
// 将返回值赋给一个局部变量
var module2 = MYAPP.namespace('MYAPP.modules.module2');
module2 === MYAPP.modules.module2; // true // 忽略最前面的'MYAPP'
MYAPP.namespace('modules.module51');

优点:有助于减少程序中所需要的全局变量的数量,并且同时还有助于避免冲突或过长的名字前缀。

缺点:

  • 需要输入更多的字符,变量加前缀,增加代码量;
  • 仅有一个全局实例,任意部分的代码都可以修改它;
  • 长嵌套名字,更长的属性解析查询时间。

解决方案:沙箱模式

2. 声明依赖模式

var myFunction = function() {
// 依赖
var event = YAHOO.util.Event,
dom = YAHOO.util.Dom; // 使用事件和DOM变量
// 下面的函数...
}

优点:

  • 显示的依赖声明表明了特定脚本文件已经包含在该页面中;
  • 在函数顶部的前期声明可以很容易发现并解析依赖;
  • 解析局部变量的速度总是要比解析全局变量(或全局变量的嵌套属性)快,提升性能;
  • 利用工具可以重命名局部变量,会生成更小的代码量。

3. 私有模式

JavaScript 中所有对象的成员是公共的。

私有成员:利用 闭包 实现。

特权方法:指可以访问私有成员的公共方法,因为它具有访问私有属性的“特殊”权限。

私有性失效:从特权方法中返回一个私有变量,且该变量恰好是一个对象或者数组,那么外面的代码仍然可以访问该私有变量,因为它通过引用传递。

1)使用构造函数获得私有性

/**
* 利用闭包实现私有成员
*/
function Gadget() {
// 私有成员
var name = "iPod";
var color = ['red']; // 公有函数
this.getName = function() {
return name;
}; // 反模式
// 不要传递需要保持私有性的对象和数组的引用
this.getColor = function() {
return color;
}; } var toy = new Gadget(); console.log(toy.name); // 'undefined'
console.log(toy.getName()); // 'iPod' var newColor = toy.getColor();
newColor[0] = 'black';
console.log(toy.getColor()); // 'black'

缺点:当将私有成员与构造函数一起使用时,每次调用构造函数以创建对象时,这些私有成员都会被重新创建。

解决方案:使用原型共享常用属性和方法,另外,还可以在多个实例中共享隐藏的实例成员。

2)模块模式的基础框架:对象字面量以及私有性
/**
* 使用对象字面量+匿名函数实现私有成员
*/
var myobj;
(function() {
// 私有成员
var name = 'my, oh my'; // 实现公有部分
// 注意,没有 'var' 修饰符
myobj = {
// 特权方法
getName: function() {
return name;
}
}
}()); myobj.getName(); // 'my, oh my' /**
* 实现2
*/
var myobj = (function() {
// 私有成员
var name = 'my, oh my'; // 实现公有部分
return {
getName: function() {
return name;
}
}
}()); myobj.getName(); // 'my, oh my'
3)原型和私有性

优点:解决构造函数获得私有性的缺点。

function Gadget() {
// 私有成员
var name = 'iPod';
// 公有函数
this.getName = function() {
return name;
};
} Gadget.prototype = (function() {
// 私有成员
var browser = "Mobile Webkit";
// 公有原型成员
return {
getBrowser: function() {
return browser;
}
};
}()); var toy = new Gadget();
toy.getName(); // 特权 'own' 方法
toy.getBrowser(); // 特权原型方法
4)将私有方法揭示为公共方法

揭示模式 可用于将私有方法暴露成公共方法。

优点:当为了对象的运转而将所有功能放置在一个对象中以及想尽可能地保护该对象是至关重要的时候,这种揭示模式就显得非常有用。

缺点:将这又私有方法暴露为公共方法时,也使它们变得更为脆弱。

/**
* 它建立在其中一种私有模式之上,即对象字面量中的私有成员
*/
var myarray;
(function() {
var astr = '[object Array]',
toString = Object.prototype.toString; function isArray(a) {
return toString.call(a) === astr;
} function indexOf(haystack, needle) {
var i = 0,
max = haystack.length;
for (; i < max; i += 1) {
if (haystack[i] === needle) {
return i;
}
}
return -1;
} myarray = { // 填充了认为适于公共访问的功能
isArray: isArray,
indexOf: indexOf, // 如果公共indexOf方法发生意外,私有indexOf方法仍然是安全的
inArray: indexOf // inArray()将继续正常运行
};
}());

4. 模块模式

  • JavaScript 并没有包的特殊语法,但是模块模式提供了一种创建自包含非耦合代码片段的有利工具,可以将它视为黑盒功能,并可以根据需求添加、替换或删除这些模块。

  • 模块模式是多种模式的组合:命名空间、即时函数、私有和特权成员、声明依赖。

优点:提供了结构化的思想并有助于组织日益增长的代码。

// 第一步:建立一个命名空间
MYAPP.namespace('MYAPP.utilities.array'); // 第二步:定义该模块
MYAPP.utilities.array = (function() { // 依赖
var uobj = MYAPP.utilities.object,
ulang = MYAPP.utilities.lang, // 私有属性
array_string = '[object Array]',
ops = Object.prototype.toString; // 私有方法
// ... // var 变量定义结束 // 可选的一次性初始化过程
// ... // 第三步:向公共接口添加一些方法
// 公有API
return {
return {
inArray: function(needle, haystack) {
// ...
},
isArray: function(a) {
// ...
}
// ... 更多方法和属性
};
}
}());
揭示模块模式

——其中所有的方法都需要保持私有性,并且只能暴露那些最后决定设立API的那些方法

MYAPP.utilities.array = (function() {

  // 私有属性
// 私有方法
isArray = function() { },
inArray = function() { }; // 揭示公有API
return {
isArray: isArray,
inArray: inArray
}
})();
创建构造函数的模块

与使用模式模式来执行创建对象的操作的区别:包装了模块的即时函数最终会返回一个函数,而不是返回一个对象。

/**
* 创建构造函数的模块
*/
MYAPP.utilities.array = (function() { // 私有属性和方法
var Constr; // 公有API——构造函数
Constr = function(o) {
this.elements = this.toArray(0);
}; // 公有API——原型
Constr.prototype = {
constructor: MYAPP.utilities.Array,
version: '2.0',
toArray: function(obj) {
for (var i = 0, a = [], len = obj.length; i < len; i += 1) {
a[i] = obj[i];
}
return a;
}
} // 返回要分配给新命名空间的构造函数
return Constr;
})(); // 使用
var arr = new MYAPP.utilities.Array(obj);
将全局变量导入到模块中

导入全局变量有助于加速即时函数中的全局符号解析的速度,因为这些导入的变量成为了该函数的局部变量。

MYAPP.utilities.module = (function(app, global) {

  // 引用全局变量
// 以及现在被转换成局部变量的
// 全局应用程序命名空间对象 }(MYAPP, this));

5. 沙箱模式

优点:解决命名空间模式的缺点。提供了一个可用于模块运行的环境,且不会对其他模块和个人沙箱造成任何影响。


静态属性和方法:指那些从一个实例到另一个实例都不会发生改变的属性和方法。

公有静态成员的实现:使用构造函数并向其添加属性。

私有静态成员:1)以同一个构造函数创建的所有对象共享该成员;2)构造函数外部不可访问该成员。

对象常量:JavaScript 使用命名规定。

/**
* 多次实例化沙箱对象的方法:
*/
new SandBox(function(box) {
// 你的代码写在这里...
}); SandBox(['ajax', 'event'], function(box) {
// console.log(box);
}); SandBox('ajax', 'dom', function(box) {
// console.log(box);
}); SandBox('*', function(box) {
// console.log(box);
}); SandBox(function(box) {
// console.log(box);
}); //===============================
/**
* 将一个模块嵌入到另一个模块中,且这两者之间不会相互干扰
*/
SandBox('dom', 'event', function(box) {
// 使用 DOM 和事件来运行
Sandbox('ajax', function(box) {
// 另一个沙箱化(sandboxed)的'box'对象
// 这里的'box'对象与函数外部的
// 'box'并不相同 //...
// 用Ajax来处理
}); // 这里没有Ajax模块
}) //===============================
// 增加模块
Sandbox.modules = {}; Sandbox.modules.dom = function(box) {
box.getElement = function() {};
box.getStyle = function() {};
box.foo = 'bar';
}; Sandbox.modules.event = function(box) {
// 如果需要,就访问Sandbox原型,如下语句
box.constructor.prototype.m = 'mmm';
box.attachEvent = function() {};
box.dettachEvent = function() P{;}
}; Sandbox.modules.ajax = function(box) {
box.makeRequest = function() {};
box.getResponse = function() {};
};
实现 Sandbox() 构造函数
function Sandbox() {
// 将参数转换成一个数组
var args = Array.prototype.slice.call(arguments),
// 最后一个参数是回调函数
callback = args.pop(),
// 模块可以作为一个数组传递,或作为单独的参数传递
modules = (args[0] && typeof args[0] === 'string') ? args : args[0],
i; // 确保该函数
// 作为构造函数被调用
if (!(this instanceof Sandbox)) {
return new Sandbox(module, callback);
} // 需要向 `this` 添加的属性
this.a = 1;
this.b = 2; // 现在向该核心`this`对象添加模块
// 不指定模块名称或指定"*"都表示"使用所有模块"
if (!module || module === '*') {
module = [];
for (i in Sandbox.modules) {
if (Sandbox.modules.hasOwnProperty(i)) {
modules.push(i);
}
}
} // 初始化所需的模块
for (i=0; i<moduels.length; i++) {
Sandbox.modules[modules[i]](this);
} // call the callback
callback(this);
} // 需要的任何原型属性
Sandbox.prototype = {
name: 'My Application',
version: '1.0',
getName: function() {
return this.name;
}
}
公有静态成员
/**
* 通过使用构造函数并向其添加属性
*/
var Gadget = function(price) {
this.price = price;
}; // 静态方法
Gadget.isShiny = function() { var msg = 'you bet'; if (this instanceof Gadget) {
msg += ', it costs $' + this.price + '!';
} return msg;
} // 向该原型添加一个普通方法
Gadget.prototype.setPrice = function() {
return Gadget.isShiny.call(this);
} // 测试静态方法调用
Gadget.isShiny(); // 'you bet' // 测试实例,非静态调用
var a = new Gadget('499.99');
a.isShiny(); // 'you bet, it costs $499.99!'
私有静态成员
var Gadget = (function() {
// 静态变量/属性
var counter = 0,
NewGadget; // 这将成为
// 新的构造函数的实现
NewGadget = function() {
counter += 1;
}; // 特权方法
NewGadget.prototype.getLastId = function() {
return counter;
} // 覆盖该构造函数
return NewGadget;
}()); var iphone = new Gadget();
iphone.getLastId(); // 1
var ipod = new Gadget();
ipod.getLastId(); // 2
var ipad = new Gadget();
ipad.getLastId(); // 3

6. 链模式

var obj = {
value: 1,
increment: function() {
this.value += 1;
return this;
},
add: function(v) {
this.value += v;
return this;
},
shout: function() {
console.log(this.value);
}
}; // 链方法调用
obj.increment().add(3).shout(); // 5

优点:节省一些输入的字符,代码更简洁,可维护性高。

缺点:更加难以调试。

7. method() 方法——语法糖

var Person = function(name) {
this.name = name;
}.
method('getName', function() {
return this.name;
}).
method('setName', function() {
this.name = name;
return this;
}); if (typeof Function.prototype.method !== 'function') {
Function.prototype.method = function(name, implementation) {
this.prototype[name] = implementation;
return this;
};
}

《JavaScript模式》第5章 对象创建模式的更多相关文章

  1. Javascript模式(第五章对象创建模式)------读书笔记

    一 命名空间模式 1 命名空间模式的代码格式 var MYAPP={ name:"", version:"1.0", init:function(){ } }; ...

  2. 设计模式---对象创建模式之原型模式(prototype)

    一:概念 原型模式(Prototype Pattern) 实际上就是动态抽取当前对象运行时的状态 Prototype模式是一种对象创建型模式,它采取复制原型对象的方法来创建对象的实例.使用Protot ...

  3. C++设计模式 之 “对象创建”模式:Factory Method、Abstract Factory、Prototype、Builder

    part 0 “对象创建”模式 通过“对象创建” 模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定.它是接口抽象之后的第一步工作. 典型模式 Fact ...

  4. 《JavaScript 模式》读书笔记(5)— 对象创建模式4

    我们学完了大部分对象创建模式相关的内容,下面还有一些小而精的部分. 七.对象常量 JavaScript中没有常量的概念,虽然许多现代的编程环境可能为您提供了用以创建常量的const语句.作为一种变通方 ...

  5. 《Javascript模式》之对象创建模式读书笔记

    引言: 在javascript中创建对象是很容易的,可以使用对象字面量或者构造函数或者object.creat.在接下来的介绍中,我们将越过这些方法去寻求一些其他的对象创建模式. 我们知道js是一种简 ...

  6. 深入理解JavaScript系列(47):对象创建模式(上篇)

    介绍 本篇主要是介绍创建对象方面的模式,利用各种技巧可以极大地避免了错误或者可以编写出非常精简的代码. 模式1:命名空间(namespace) 命名空间可以减少全局命名所需的数量,避免命名冲突或过度. ...

  7. 深入理解JavaScript系列(48):对象创建模式(下篇)

    介绍 本篇主要是介绍创建对象方面的模式的下篇,利用各种技巧可以极大地避免了错误或者可以编写出非常精简的代码. 模式6:函数语法糖 函数语法糖是为一个对象快速添加方法(函数)的扩展,这个主要是利用pro ...

  8. 《JavaScript 模式》读书笔记(5)— 对象创建模式1

    这又是一个新的开始,对象的重要性不言而喻.在JavaScript中创建对象是十分容易的,之前聊过的对象字面量和构造函数都可以达到目的.但是本篇中,我们越过那些方法,以寻求一些额外的对象创建模式. 本篇 ...

  9. 设计模式---对象创建模式之构建器模式(Builder)

    一:概念 Builder模式也叫建造者模式或者生成器模式,是由GoF提出的23种设计模式中的一种.Builder模式是一种对象创建型模式之一,用来隐藏复合对象的创建过程,它把复合对象的创建过程加以抽象 ...

随机推荐

  1. Python多进程编程

    转自:Python多进程编程 阅读目录 1. Process 2. Lock 3. Semaphore 4. Event 5. Queue 6. Pipe 7. Pool 序. multiproces ...

  2. CentOS7 基础配置

    Centos 7 部分>>>>>>>>>>>>>>>>>>>>>>& ...

  3. Java-简陋的图书管理

    本文代码为原创一个简陋的管理系统,只做功能的测试.并没有去完善所有应有的功能,只做了输入输出查找.仅供参考! 菜单部分: import java.util.Scanner; public class ...

  4. 单调队列 hdu2823

    Sliding Window Time Limit: 12000MS   Memory Limit: 65536K Total Submissions: 48608   Accepted: 14047 ...

  5. createjs 的 bitmapdata类

    今天测试一个功能,在效率上出现了问题.2D舞台绘制了大量的元素,联想到AS3的 bitmapdata.darw() 功能,遗憾是createjs官方类 中没有bitmapdata类. 好在已经有大神替 ...

  6. 《BI项目笔记》创建多维数据集Cube(1)

    有两个事实表,因此就有两个度量值组,并且向导将为非维度键的事实表中的每一个数值列创建一个度量值.由于我们这里不需要那么多,所以只选择部分度量值.另外要注意,度量值的名称源于事实表中的列,所有名称由可能 ...

  7. struts(二) ---中参数传值

    struts中参数传值的方式有 种: 第一种:直接通过属性来传值 第二种: 第三种:

  8. [WebLoad] 使用WebLoad进行Web Application 性能测试的流程

    1. 打开WebLOAD IDE录制或编写一个脚本文件,成功后会生成一个后缀为“.wlp”的文件. 2. 打开WebLOAD Console创建一个Load Template,创建过程当中需要添加“. ...

  9. Modify textures at runtime

    动态修改Texture Modify textures at runtime?http://answers.unity3d.com/questions/7906/modify-textures-at- ...

  10. Javascript之运动框架2

    运动框架2与运动框架1的不同之处在于,运动框架2是框架1的升级版,首先完善了传入值,改为move(obj,json,options),在options里面,可以选择传入或者不传入时间,运动形式,以及函 ...