当你不再满足于仅使用 jQuery 为网站添加简单的增强功能,而开始开发成熟的客户端应用程序时,你需要考虑如何组织你的代码。在本章中,我们将介绍可以在 jQuery 应用中使用的各种代码组织模式,并探索 RequireJS 依赖管理和构建系统。
链接 核心概念
在深入研究代码组织模式之前,理解一些所有优秀的组织模式所共有的概念非常重要。
- 你的代码应该被划分为功能单元——模块、服务等。不要经受不住诱惑而将所有代码都写在一个巨大的
$( document ).ready()块中。这个概念通俗地被称为“封装”。 - 不要重复自己(DRY)。找出各功能块之间的相似之处,并利用继承技术来避免重复代码。
- 尽管 jQuery 具有以 DOM 为中心的特性,但 JavaScript 应用程序并不完全是关于 DOM 的。请记住,并非所有的功能单元都需要(或应该)有 DOM 表现形式。
- 功能单元应该是松耦合的,也就是说,一个功能单元应该能够独立存在,而单元之间的通信应通过消息系统(如自定义事件或发布/订阅模式)来处理。尽可能避免功能单元之间的直接通信。
对于初次尝试开发复杂应用程序的开发者来说,松耦合的概念可能尤其令人困扰,因此在开始时请务必留意这一点。
链接 封装
代码组织的第一步是将应用程序划分为不同的部分;有时,仅仅这一项努力就足以改善代码结构及其可维护性。
链接 对象字面量
对象字面量(Object Literal)或许是封装相关代码最简单的方法。它不提供属性或方法的私密性,但对于消除代码中的匿名函数、集中配置选项以及简化重用和重构路径非常有用。
|
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
|
|
上面的对象字面量只是一个赋值给变量的对象。该对象有一个属性和多个方法。所有的属性和方法都是公开的,因此应用程序的任何部分都可以查看属性并调用该对象的方法。虽然有一个 init 方法,但并没有规定在对象生效前必须调用它。
我们如何将这种模式应用于 jQuery 代码?假设我们有一段以传统 jQuery 风格编写的代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
|
如果这就是我们应用程序的全部内容,那么保持原样也是可以的。另一方面,如果这是更大规模应用程序的一部分,我们最好将此功能与无关功能分开。我们可能还想将 URL 移出代码并放入配置区域。最后,我们可能想打破链式调用,以便稍后更容易修改功能片段。
|
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
|
|
你首先会注意到的是,这种方法显然比原始代码长得多——再次强调,如果这就是我们应用的全部,使用对象字面量可能大材小用了。但假设它不是应用的全部,我们已经获得了几点好处:
- 我们将功能拆分成了微小的方法。未来,如果我们想改变内容的展示方式,很容易知道在哪里修改。在原始代码中,这一步很难定位。
- 我们消除了匿名函数的使用。
- 我们将配置选项从代码主体中移出,放在了一个中心位置。
- 我们消除了链式调用的约束,使代码更容易重构、混合和重新排列。
对于非简单的功能,对象字面量明显优于塞在 $( document ).ready() 块中的长段代码,因为它们促使我们思考功能的组成部分。然而,它们并不比在 $( document ).ready() 块内部简单地声明一堆函数先进多少。
链接 模块模式
模块模式(Module Pattern)克服了对象字面量的一些局限性,它为变量和函数提供了私密性,同时可以根据需要公开公共 API。
|
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
|
|
在上面的示例中,我们自执行一个返回对象的匿名函数。在函数内部,我们定义了一些变量。因为变量是在函数内部定义的,除非我们将它们放入返回的对象中,否则在函数外部无法访问它们。这意味着函数外部的代码无法访问 privateThing 变量或 changePrivateThing 函数。然而,sayPrivateThing 可以访问 privateThing 和 changePrivateThing,因为它们都定义在与 sayPrivateThing 相同的作用域内。
这种模式非常强大,因为正如你从变量名中可以看出的,它可以为你提供私有变量和函数,同时通过返回对象的属性和方法暴露有限的 API。
下面是前一个示例的修订版本,展示了我们如何使用模块模式创建相同的功能,同时只暴露模块的一个公共方法:showItemByIndex()。
|
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
|
|