【书名】:你不知道的JavaScript(上卷)
【作者】:Kyle Simpson
【本书总页码】:213
【已读页码】:73
单例模式
将模块函数转换成 IIFE,立即调用这个函数并将返回值直接赋值给单例的模块实例标识符 foo。
参数实例化模块
模块也是普通的函数,因此可以接受参数,通过参数来创建不同的实例子。
返回命名对象作为公共API
通过在模块实例的内部保留对公共 API 对象的内部引用,可以从内部对模块实例进行修改,包括添加或删除方法和属性,以及修改它们的值。
现代的模块机制
大多数模块依赖加载器 / 管理器本质上都是将这种模块定义封装进一个友好的 API。
这段代码的核心是 modules[name] = impl.apply(impl, deps)。为了模块的定义引入了包装函数(可以传入任何依赖),并且将返回值,也就是模块的 API,储存在一个根据名字来管理的模块列表中。
看看它如何使用它来定义模块:
"foo" 和 "bar" 模块都是通过一个返回公共 API 的函数来定义的。"foo" 甚至接受 "bar" 的示例作为依赖参数,并能相应地使用它。
为我们自己着想,应该多花一点时间来研究这些示例代码并完全理解闭包的作用。最重要的是要理解模块管理器没有任何特殊的“魔力”。它们符合前面列出的模块模式的两个特点:为函数定义引入包装函数,并保证它的返回值和模块的 API 保持一致。
未来的模块机制
ES6 中为模块增加了一级语法支持。但通过模块系统进行加载时,ES6 会将文件当作独立的模块来处理。每个模块都可以导入其他模块或特定的 API 成员,同样也可以导出自己的API 成员。
基于函数的模块并不是一个能被稳定识别的模式(编译器无法识别),它们的 API 语义只有在运行时才会被考虑进来。因此可以在运行时修改一个模块的 API。
相比之下,ES6 模块 API 更加稳定(API 不会在运行时改变)。由于编译器知道这一点,因此可以在编译期检查对导入模块的 API 成员的引用是否真实存在。如果 API 引用并不存在,编译器会在运行时抛出一个或多个“早期”错误,而不会像往常一样在运行期采用动态的解决方案。
ES6 的模块没有“行内”格式,必须被定义在独立的文件中(一个文件一个模块)。浏览器或引擎有一个默认的“模块加载器”可以在导入模块时异步地加载模块文件。
需要用前面两个代码片段中的内容分别创建文件 foo.js 和 bar.js。然后如第三个代码片段中展示的那样,baz.js 中的程序会加载或导入这两个模块并使用它们。
import 可以将一个模块中的一个或多个 API 导入到当前作用域中,并分别绑定在一个变量上。module 会将整个模块的 API 导入并绑定到一个变量上。export 会将当前模块的一个标识符(变量、函数)导出为公共 API。这些操作可以在模块定义中根据需要使用任意多次。
模块文件中的内容会被当作好像包含在作用域闭包中一样来处理。