Javascript模式之四-对象创建模式

命名空间模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var MYAPP = MYAPP || {};
MYAPP.namespace = function(ns_string){
var parts = ns_string.split('.');
var parent = MYAPP;

//剥离最前面的冗余全局变量
if(parts[0] === 'MYAPP'){
parts = parts.slice(1);
}

for(var 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

模块模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
MYAPP.namespace('MYAPP.utilities.array');
MYAPP.utilities.array = (function(){
//依赖
var uobj = MYAPP.utilities.object;
var ulang = MYAPP.utilities.lang;

//私有属性
var array_string = '[object Array]';
var ops = Object.prototype.toString;

//私有方法
//...

//可选的一次性初始化过程
//...

//公有API
return {
isArray: function(a){
return ops.call(a) === array_string;
},
//更多方法和属性...
};
}());

模块模式的变体
揭示模块模式:所有的方法都需要保持私有性,并且只能暴露那些最后决定设立API的那些方法。
创建构造函数的模块:包装了模块的即时函数最终将会返回一个函数,而不是返回一个对象。
将全局变量导入到模块中:将全局变量以参数的形式传递到包装了模块的即时函数中。

沙箱模式

沙箱模式提供了一个可用于模块运行的环境,且不会对其他模块和个人沙箱造成任何影响。
在命名空间模式中,有一个全局对象。在沙箱模式中,则是一个全局构造函数,让我们称之为Sandbox()。可以使用该构造函数创建对象并且还可以传递回调函数,它变成了代码的隔离沙箱运行环境。

使用示例

1
2
3
4
5
6
7
8
9
10
Sandbox(['dom', 'event'], function(box){
//使用DOM和事件来运行...

Sandbox('ajax', function(box){
//另一个沙箱
//使用Ajax来处理...
});

//这里没有Ajax模块
});

使用本沙箱模式时,可以通过将代码包装到回调函数中从而保护全局命名空间。可以根据所需要的模块类型创建不同的沙箱实例,并且这些实例互相独立运行。

具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
function Sandbox(){
//将参数转换成一个数组
var args = Array.prototype.slice.call(arguments);
//最后一个参数是回调函数
var callback = args.pop();
//模块可以作为一个数组传递,或作为单独的参数传递
var modules = (args[0] && typeof args[0] === 'string') ? args : args[0];

//确保该函数作为够咱函数被调用
//参见"Javascript模式之二"中的"强制使用new模式"
if(!(this instanceof Sandbox)){
return new Sandbox(modules, callback);
}

//需要向this添加的属性
//example
this.a = 1;
this.b = 2;

//现在向该核心this对象添加模块
//不指定模块名称或制定'*',都表示使用所有模块
if(!modules || modules === '*'){
modules = [];
for(i in Sandbox.modules){
if(Sandbox.modules.hasOwnProperty(i)){
modules.push(i);
}
}
}

//初始化所需的模块
for(var i=0; i<modules.length; i++){
Sandbox.modules[modules[i]](this);
}

//回调
//注:改变的是this即回调函数的参数box所拥有的属性和方法
callback(this);
}

//需要的任何原型属性
Sandbox.prototype = {
name: 'My Application',
version: '1.0',
getName: function(){
return this.name;
}
};

//添加模块
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(){};
};

Sandbox.modules.ajax = function(box){
box.makeRequest = function(){};
box.getResponse = function(){};
};

在这个示例实现中,我们并不关心从其他文件中加载所需的功能,但这绝对也是一个可选的实现功能。比如YUI3库中就支持这种功能。
当我们知道所需的模块时,便可以据此进行初始化,这表示可以调用实现每个模块的函数。
该构造函数的最后一个参数是一个回调函数。该回调函数将会在使用新创建的实例时最后被调用。这个回调函数实际上是用户的沙箱,它可以获得一个填充了所需功能的box对象。

我觉得沙箱模式的缺点是Sandbox.modules暴露在外,应该使用更好的AMD(Asynchronous Module Definition)模式

链模式

链模式的优点在于它可以帮助你考虑分割函数,以创建更加简短、具有特定功能的函数,而不是创建尝试实现太多功能的函数。从长远看来,这提高了代码的可维护性。
链模式的缺点在于以这种方式编写的代码更加难以调试。或许知道在某个特定的代码行中发生错误,但是在此行中实际执行了太多的步骤。当链中多个方法的其中一个静默失效时,无法知道是哪一个方法发生失效。
在任何情况下,识别出这种模式都很有好处。当编写的方法并没有明显和有意义的返回值时,可以总是返回this

method()方法思想(并不是很推荐)

example.

1
2
3
4
5
6
7
8
9
10
11
12
13
var Person = function(name){
this.name = name;
}.
method('getName', function(){
return this.name;
}).
method('setName', function(name){
this.name = name;
return this;
});

var a = new Person('Adam');
a.setName('Eve').getName();

method()方法实现

1
2
3
4
5
6
if(typeof Function.prototype.method !== 'function'){
Function.prototype.method = function(name, implementation){
this.prototype[name] = implementation;
return this.
}
}

参考

JavaScript模式