1
0
mirror of https://github.com/apachecn/eloquent-js-3e-zh.git synced 2025-05-23 20:02:20 +00:00
This commit is contained in:
wizardforcel 2018-05-08 17:31:37 +08:00
parent eef0576d16
commit 013d79452d

82
10.md Normal file
View File

@ -0,0 +1,82 @@
# 十、模块
> 编写易于删除,而不是易于扩展的代码。
> Tef《Programming is Terrible》
理想的程序拥有清晰的结构。 它的工作方式很容易解释,每个部分都起到明确的作用。
典型的真实程序会有机地增长。 新功能随着新需求的出现而增加。 构建和维护结构是额外的工作,只有在下一次有人参与该计划时,才会得到回报。 所以它易于忽视,并让程序的各个部分变得深深地纠缠在一起。
这导致了两个实际问题。 首先,这样的系统难以理解。 如果一切都可以接触到一切其它东西,那么很难单独观察任何给定的片段。 你不得不全面理解整个东西。 其次,如果你想在另一个场景中,使用这种程序的任何功能,比起试图从它的上下文中将它分离出来,重写它可能要容易。
术语“大泥球”通常用于这种大型,无结构的程序。 一切都粘在一起,当你试图挑选出一段代码时,整个东西就会分崩离析,你的手会变脏。
## 模块
模块试图避免这些问题。 模块是一个程序片段,规定了它依赖的其他部分,以及它为其他模块提供的功能(它的接口)。
模块接口与对象接口有许多共同之处,我们在第 6 章中看到。它们向外部世界提供模块的一部分,并使其余部分保持私有。 通过限制模块彼此交互的方式,系统变得更像积木,其中的组件通过明确定义的连接器进行交互,而不像泥浆一样,一切都混在一起。
模块之间的关系称为依赖关系。 当一个模块需要另一个模块的片段时,就说它依赖于这个模块。 当模块中明确规定了这个事实时,它可以用于确定,需要哪些其他模块才能使用给定的模块,并自动加载依赖关系。
为了以这种方式分离模块,每个模块需要它自己的私有作用域。
将您的 JavaScript 代码放入不同的文件,不能满足这些要求。 这些文件仍然共享相同的全局命名空间。 他们可以有意或无意干扰彼此的绑定。 依赖性结构仍不清楚。 我们将在本章后面看到,我们可以做得更好。
合适的模块结构可能难以为程序设计。 在你还在探索这个问题的阶段,尝试不同的事情来看看什么是可行的,你可能不想过多担心它,因为这可能让你分心。 一旦你有一些感觉可靠的东西,现在是后退一步并组织它的好时机。
## 包
从单独的片段中构建一个程序,并实际上能够独立运行这些片段的一个优点是,您可能能够在不同的程序中应用相同的部分。
但如何实现呢? 假设我想在另一个程序中使用第 9 章中的`parseINI`函数。 如果清楚该函数依赖什么(在这种情况下什么都没有),我可以将所有必要的代码复制到我的新项目中并使用它。 但是,如果我在代码中发现错误,我可能会在当时正在使用的任何程序中将其修复,并忘记在其他程序中修复它。
一旦你开始复制代码,你很快就会发现,自己在浪费时间和精力来到处复制并使他们保持最新。
这就是包的登场时机。包是可分发(复制和安装)的一大块代码。 它可能包含一个或多个模块,并且具有关于它依赖于哪些其他软件包的信息。 一个包通常还附带说明它做什么的文档,以便那些不编写它的人仍然可以使用它。
在包中发现问题或添加新功能时,会将包更新。 现在依赖它的程序(也可能是包)可以升级到新版本。
以这种方式工作需要基础设施。 我们需要一个地方来存储和查找软件包,以及一个便利方式来安装和升级它们。 在 JavaScript 世界中,这个基础结构由 [NPM](https://npmjs.org) 提供。
NPM 是两个东西:可下载(和上传)包的在线服务,以及可帮助您安装和管理它们的程序(与 Node.js 捆绑在一起)。
在撰写本文时NPM 上有超过 50 万个不同的包。 其中很大一部分是垃圾,我应该提一下,但几乎所有有用的公开包都可以在那里找到。 例如,一个 INI 文件解析器,类似于我们在第 9 章中构建的那个,可以在包名称`ini`下找到。
第 20 章将介绍如何使用`npm`命令行程序在本地安装这些包。
使优质的包可供下载是非常有价值的。 这意味着我们通常可以避免重新创建一百人之前写过的程序,并在按下几个键时得到一个可靠,充分测试的实现。
软件的复制很便宜,所以一旦有人编写它,分发给其他人是一个高效的过程。但首先把它写出来是工作量,回应在代码中发现问题的人,或者想要提出新功能的人,是更大的工作量。
默认情况下,您拥有您编写的代码的版权,其他人只有经过您的许可才能使用它。但是因为有些人不错,而且由于发布好的软件可以使你在程序员中出名,所以许多软件包都会在许可证下发布,明确允许其他人使用它。
NPM 上的大多数代码都以这种方式授权。某些许可证要求您还要在相同许可证下发布基于那个包构建的代码。其他要求不高,只是要求在分发代码时保留许可证。 JavaScript 社区主要使用后一种许可证。使用其他人的软件包时,请确保您留意了他们的许可证。
## 即兴的模块
2015 年之前JavaScript 语言没有内置的模块系统。 然而,尽管人们已经用 JavaScript 构建了十多年的大型系统,他们需要模块。
所以他们在语言之上设计了自己的模块系统。 您可以使用 JavaScript 函数创建本地作用域,并使用对象来表示模块接口。
这是一个模块,用于日期名称和数字之间的转换(由`Date``getDay`方法返回)。 它的接口由`weekDay.name``weekDay.number`组成,它将本地绑定名称隐藏在立即调用的函数表达式的作用域内。
```js
const weekDay = function() {
const names = ["Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"];
return {
name(number) { return names[number]; },
number(name) { return names.indexOf(name); }
};
}();
console.log(weekDay.name(weekDay.number("Sunday")));
// → Sunday
```
这种风格的模块在一定程度上提供了隔离,但它不声明依赖关系。 相反,它只是将其接口放入全局范围,并希望它的依赖关系(如果有的话)也这样做。 很长时间以来,这是 Web 编程中使用的主要方法,但现在它几乎已经过时。
如果我们想让依赖关系成为代码的一部分,我们必须控制依赖关系的加载。 实现它需要能够将字符串执行为代码。 JavaScript 可以做到这一点。