mirror of
https://github.com/apachecn/eloquent-js-3e-zh.git
synced 2025-05-23 20:02:20 +00:00
10.
This commit is contained in:
parent
013d79452d
commit
74bdbcf926
99
10.md
99
10.md
@ -34,33 +34,33 @@
|
||||
|
||||
一旦你开始复制代码,你很快就会发现,自己在浪费时间和精力来到处复制并使他们保持最新。
|
||||
|
||||
这就是包的登场时机。包是可分发(复制和安装)的一大块代码。 它可能包含一个或多个模块,并且具有关于它依赖于哪些其他软件包的信息。 一个包通常还附带说明它做什么的文档,以便那些不编写它的人仍然可以使用它。
|
||||
这就是包的登场时机。包是可分发(复制和安装)的一大块代码。 它可能包含一个或多个模块,并且具有关于它依赖于哪些其他包的信息。 一个包通常还附带说明它做什么的文档,以便那些不编写它的人仍然可以使用它。
|
||||
|
||||
在包中发现问题或添加新功能时,会将包更新。 现在依赖它的程序(也可能是包)可以升级到新版本。
|
||||
|
||||
以这种方式工作需要基础设施。 我们需要一个地方来存储和查找软件包,以及一个便利方式来安装和升级它们。 在 JavaScript 世界中,这个基础结构由 [NPM](https://npmjs.org) 提供。
|
||||
以这种方式工作需要基础设施。 我们需要一个地方来存储和查找包,以及一个便利方式来安装和升级它们。 在 JavaScript 世界中,这个基础结构由 [NPM](https://npmjs.org) 提供。
|
||||
|
||||
NPM 是两个东西:可下载(和上传)包的在线服务,以及可帮助您安装和管理它们的程序(与 Node.js 捆绑在一起)。
|
||||
|
||||
在撰写本文时,NPM 上有超过 50 万个不同的包。 其中很大一部分是垃圾,我应该提一下,但几乎所有有用的公开包都可以在那里找到。 例如,一个 INI 文件解析器,类似于我们在第 9 章中构建的那个,可以在包名称`ini`下找到。
|
||||
|
||||
第 20 章将介绍如何使用`npm`命令行程序在本地安装这些包。
|
||||
第 20 章将介绍如何使用`npm`命令行程序在局部安装这些包。
|
||||
|
||||
使优质的包可供下载是非常有价值的。 这意味着我们通常可以避免重新创建一百人之前写过的程序,并在按下几个键时得到一个可靠,充分测试的实现。
|
||||
|
||||
软件的复制很便宜,所以一旦有人编写它,分发给其他人是一个高效的过程。但首先把它写出来是工作量,回应在代码中发现问题的人,或者想要提出新功能的人,是更大的工作量。
|
||||
|
||||
默认情况下,您拥有您编写的代码的版权,其他人只有经过您的许可才能使用它。但是因为有些人不错,而且由于发布好的软件可以使你在程序员中出名,所以许多软件包都会在许可证下发布,明确允许其他人使用它。
|
||||
默认情况下,您拥有您编写的代码的版权,其他人只有经过您的许可才能使用它。但是因为有些人不错,而且由于发布好的软件可以使你在程序员中出名,所以许多包都会在许可证下发布,明确允许其他人使用它。
|
||||
|
||||
NPM 上的大多数代码都以这种方式授权。某些许可证要求您还要在相同许可证下发布基于那个包构建的代码。其他要求不高,只是要求在分发代码时保留许可证。 JavaScript 社区主要使用后一种许可证。使用其他人的软件包时,请确保您留意了他们的许可证。
|
||||
NPM 上的大多数代码都以这种方式授权。某些许可证要求您还要在相同许可证下发布基于那个包构建的代码。其他要求不高,只是要求在分发代码时保留许可证。 JavaScript 社区主要使用后一种许可证。使用其他人的包时,请确保您留意了他们的许可证。
|
||||
|
||||
## 即兴的模块
|
||||
|
||||
2015 年之前,JavaScript 语言没有内置的模块系统。 然而,尽管人们已经用 JavaScript 构建了十多年的大型系统,他们需要模块。
|
||||
|
||||
所以他们在语言之上设计了自己的模块系统。 您可以使用 JavaScript 函数创建本地作用域,并使用对象来表示模块接口。
|
||||
所以他们在语言之上设计了自己的模块系统。 您可以使用 JavaScript 函数创建局部作用域,并使用对象来表示模块接口。
|
||||
|
||||
这是一个模块,用于日期名称和数字之间的转换(由`Date`的`getDay`方法返回)。 它的接口由`weekDay.name`和`weekDay.number`组成,它将本地绑定名称隐藏在立即调用的函数表达式的作用域内。
|
||||
这是一个模块,用于日期名称和数字之间的转换(由`Date`的`getDay`方法返回)。 它的接口由`weekDay.name`和`weekDay.number`组成,它将局部绑定名称隐藏在立即调用的函数表达式的作用域内。
|
||||
|
||||
```js
|
||||
const weekDay = function() {
|
||||
@ -80,3 +80,88 @@ console.log(weekDay.name(weekDay.number("Sunday")));
|
||||
|
||||
如果我们想让依赖关系成为代码的一部分,我们必须控制依赖关系的加载。 实现它需要能够将字符串执行为代码。 JavaScript 可以做到这一点。
|
||||
|
||||
## 将数据执行为代码
|
||||
|
||||
有几种方法可以将数据(代码的字符串)作为当前程序的一部分运行。
|
||||
|
||||
最明显的方法是特殊运算符`eval`,它将在当前作用域内执行一个字符串。 这通常是一个坏主意,因为它破坏了作用域通常拥有的一些属性,比如易于预测给定名称所引用的绑定。
|
||||
|
||||
```js
|
||||
const x = 1;
|
||||
function evalAndReturnX(code) {
|
||||
eval(code);
|
||||
return x;
|
||||
}
|
||||
|
||||
console.log(evalAndReturnX("var x = 2"));
|
||||
// → 2
|
||||
console.log(x);
|
||||
// → 1
|
||||
```
|
||||
|
||||
将数据解释为代码的不太可怕的方法,是使用`Function`构造器。 它有两个参数:一个包含逗号分隔的参数名称列表的字符串,和一个包含函数体的字符串。 它将代码封装在一个函数值中,以便它获得自己的作用域,并且不会对其他作用域做出奇怪的事情。
|
||||
|
||||
```py
|
||||
let plusOne = Function("n", "return n + 1;");
|
||||
console.log(plusOne(4));
|
||||
// → 5
|
||||
```
|
||||
|
||||
这正是我们需要的模块系统。 我们可以将模块的代码包装在一个函数中,并将该函数的作用域用作模块作用域。
|
||||
|
||||
## CommonJS
|
||||
|
||||
用于连接 JavaScript 模块的最广泛的方法称为 CommonJS 模块。 Node.js 使用它,并且是 NPM 上大多数包使用的系统。
|
||||
|
||||
CommonJS 模块的主要概念是称为`require`的函数。 当您使用依赖项的模块名称调用这个函数时,它会确保该模块已加载并返回其接口。
|
||||
|
||||
由于加载器将模块代码封装在一个函数中,模块自动得到它们自己的局部作用域。 他们所要做的就是,调用`require`来访问它们的依赖关系,并将它们的接口放在绑定到`exports`的对象中。
|
||||
|
||||
此示例模块提供了日期格式化功能。 它使用 NPM的两个软件包,`ordinal`用于将数字转换为字符串,如`"1st"`和`"2nd"`,以及`date-names`用于获取星期和月份的英文名称。 它导出函数`formatDate`,它接受一个`Date`对象和一个模板字符串。
|
||||
|
||||
模板字符串可包含指明格式的代码,如`YYYY`用于全年,`Do`用于每月的序数日。 你可以给它一个像`"MMMM Do YYYY"`这样的字符串,来获得像`"November 22nd 2017"`这样的输出。
|
||||
|
||||
```js
|
||||
const ordinal = require("ordinal");
|
||||
const {days, months} = require("date-names");
|
||||
|
||||
exports.formatDate = function(date, format) {
|
||||
return format.replace(/YYYY|M(MMM)?|Do?|dddd/g, tag => {
|
||||
if (tag == "YYYY") return date.getFullYear();
|
||||
if (tag == "M") return date.getMonth();
|
||||
if (tag == "MMMM") return months[date.getMonth()];
|
||||
if (tag == "D") return date.getDate();
|
||||
if (tag == "Do") return ordinal(date.getDate());
|
||||
if (tag == "dddd") return days[date.getDay()];
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
`ordinal`的接口是单个函数,而`date-names`导出包含多个东西的对象 - `days`和`months`是名称数组。 为导入的接口创建绑定时,解构是非常方便的。
|
||||
|
||||
该模块将其接口函数添加到`exports`,以便依赖它的模块可以访问它。 我们可以像这样使用模块:
|
||||
|
||||
```js
|
||||
const {formatDate} = require("./format-date");
|
||||
|
||||
console.log(formatDate(new Date(2017, 9, 13),
|
||||
"dddd the Do"));
|
||||
// → Friday the 13th
|
||||
```
|
||||
|
||||
我们可以用最简单的形式定义`require`,如下所示:
|
||||
|
||||
```js
|
||||
require.cache = Object.create(null);
|
||||
|
||||
function require(name) {
|
||||
if (!(name in require.cache)) {
|
||||
let code = readFile(name);
|
||||
let module = {exports: {}};
|
||||
require.cache[name] = module;
|
||||
let wrapper = Function("require, exports, module", code);
|
||||
wrapper(require, module.exports, module);
|
||||
}
|
||||
return require.cache[name].exports;
|
||||
}
|
||||
```
|
||||
|
Loading…
x
Reference in New Issue
Block a user