做者:Dixin翻译:疯狂的技术宅javascript
https://weblogs.asp.net/dixin...html
未经容许严禁转载前端
JavaScript 语言最初是为简单的表单操做而发明的,没有诸如模块或命名空间之类的内置功能。多年以来发明了大量的术语、模式、库、语法和工具来模块化 JavaScript。本文讨论了 JavaScript 中的全部主流模块系统、格式、库和工具,包括:java
JavaScript 模块格式和工具大全webpack
IIFE 模块:JavaScript 模块模式git
AMD 模块:异步模块定义或 RequireJS 模块程序员
UMD 模块:通用模块定义或 UmdJS 模块github
系统模块:SystemJS 模块web
Babel 模块:从 ES 模块转换面试
TypeScript 模块:转换为 CJS、AMD、ES、系统模块
但愿本文能够帮助你了解和使用 JavaScript/TypeScript 语言,RequireJS/SystemJS 库和 Webpack/Babel 工具等全部这些模式。
在浏览器中,定义 JavaScript 变量就是定义全局变量,这会致使当前网页所加载的所有 JavaScript 文件之间的污染:
// Define global variables. let count = 0; const increase = () => ++count; const reset = () => { count = 0; console.log("Count is reset."); }; // Use global variables. increase(); reset();
为了不全局污染,能够用匿名函数来包装代码:
(() => { let count = 0; // ... });
显然,这样就再也不有任何全局变量。可是定义函数不会在函数内部执行代码。
为了执行函数 f
中的代码,语法是将函数调用 ()
做为 f()
。为了在匿名函数 (() => {})
中执行代码,能够将相同的函数调用语法 ()
用做 (() => {})
:
(() => { let count = 0; // ... })();
这称为 IIFE(当即调用的函数表达式)。所以,能够经过如下方式定义基本模块:
// Define IIFE module. const iifeCounterModule = (() => { let count = 0; return { increase: () => ++count, reset: () => { count = 0; console.log("Count is reset."); } }; })(); // Use IIFE module. iifeCounterModule.increase(); iifeCounterModule.reset();
它将模块代码包装在 IIFE 中,返回一个对象,这个对象是导出的 API 的占位符。仅引入 1 个全局变量,这是模式名称。以后模块名可用于调用导出的模块 API。这称为 JavaScript 的模块模式。
定义模块时,可能须要一些依赖关系。使用 IIFE 模块模式,其余全部模块都是全局变量。它们能够在匿名函数内部直接访问,也能够经过匿名函数的参数进行传递:
// Define IIFE module with dependencies. const iifeCounterModule = ((dependencyModule1, dependencyModule2) => { let count = 0; return { increase: () => ++count, reset: () => { count = 0; console.log("Count is reset."); } }; })(dependencyModule1, dependencyModule2);
一些流行库(如 jQuery)的早期版本遵循这种模式。
揭示模块模式由 Christian Heilmann 命名。此模式也是 IIFE,但它强调将全部 API 定义为匿名函数内的局部变量:
// Define revealing module. const revealingCounterModule = (() => { let count = 0; const increase = () => ++count; const reset = () => { count = 0; console.log("Count is reset."); }; return { increase, reset }; })(); // Use revealing module. revealingCounterModule.increase(); revealingCounterModule.reset();
用这种语法,当 API 须要相互调用时,将会变得更加容易。
CommonJS(最初名为 ServerJS)是定义和使用模块的模式。它由 Node.js 实现。默认状况下,每一个 .js 文件都是 CommonJS 模块。为模块提供了暴露 API 的模块变量和导出变量。而且提供了一个 require 函数来使用模块。如下代码以 CommonJS 语法定义了 counter
模块:
// Define CommonJS module: commonJSCounterModule.js. const dependencyModule1 = require("./dependencyModule1"); const dependencyModule2 = require("./dependencyModule2"); let count = 0; const increase = () => ++count; const reset = () => { count = 0; console.log("Count is reset."); }; exports.increase = increase; exports.reset = reset; // Or equivalently: module.exports = { increase, reset };
如下例子使用了 counter
模块:
// Use CommonJS module. const { increase, reset } = require("./commonJSCounterModule"); increase(); reset(); // Or equivelently: const commonJSCounterModule = require("./commonJSCounterModule"); commonJSCounterModule.increase(); commonJSCounterModule.reset();
在运行时,Node.js 经过将文件内的代码包装到一个函数中,而后经过参数传递 exports
变量、module
变量和 require
函数来实现这一目的。
// Define CommonJS module: wrapped commonJSCounterModule.js. (function (exports, require, module, __filename, __dirname) { const dependencyModule1 = require("./dependencyModule1"); const dependencyModule2 = require("./dependencyModule2"); let count = 0; const increase = () => ++count; const reset = () => { count = 0; console.log("Count is reset."); }; module.exports = { increase, reset }; return module.exports; }).call(thisValue, exports, require, module, filename, dirname); // Use CommonJS module. (function (exports, require, module, __filename, __dirname) { const commonJSCounterModule = require("./commonJSCounterModule"); commonJSCounterModule.increase(); commonJSCounterModule.reset(); }).call(thisValue, exports, require, module, filename, dirname);
AMD(Asynchronous Module Definition https://github.com/amdjs/amdj...)是一种定义和使用模块的模式。它由 RequireJS 库(https://requirejs.org/)实现。 AMD 提供了一个定义模块的定义函数,该函数接受模块名称、依赖模块的名称以及工厂函数:
// Define AMD module. define("amdCounterModule", ["dependencyModule1", "dependencyModule2"], (dependencyModule1, dependencyModule2) => { let count = 0; const increase = () => ++count; const reset = () => { count = 0; console.log("Count is reset."); }; return { increase, reset }; });
它还提供了 require 函数来使用模块:
// Use AMD module. require(["amdCounterModule"], amdCounterModule => { amdCounterModule.increase(); amdCounterModule.reset(); });
AMD 的 require
函数与 CommonJS 的 require
函数彻底不一样。 AMD 的 require
接受要使用的模块的名称,并将模块传递给函数参数。
AMD 的 require
函数还有另外一个重载。它接受一个回调函数,并将相似 CommonJS 的 require
函数传递给该回调。因此能够经过调用 require
来加载 AMD 模块:
// Use dynamic AMD module. define(require => { const dynamicDependencyModule1 = require("dependencyModule1"); const dynamicDependencyModule2 = require("dependencyModule2"); let count = 0; const increase = () => ++count; const reset = () => { count = 0; console.log("Count is reset."); }; return { increase, reset }; });
上面的 define
函数有一个重载,它能够传递 require
函数,并将变量和模块变量导出到回调中,以便 CommonJS 代码能够在其内部工做:
// Define AMD module with CommonJS code. define((require, exports, module) => { // CommonJS code. const dependencyModule1 = require("dependencyModule1"); const dependencyModule2 = require("dependencyModule2"); let count = 0; const increase = () => ++count; const reset = () => { count = 0; console.log("Count is reset."); }; exports.increase = increase; exports.reset = reset; }); // Use AMD module with CommonJS code. define(require => { // CommonJS code. const counterModule = require("amdCounterModule"); counterModule.increase(); counterModule.reset(); });
UMD(Universal Module Definition,https://github.com/umdjs/umd)是一组棘手的模式,可使你的代码文件在多种环境中工做。
例如如下是一种 UMD 模式,可以使模块定义可用于 AMD(RequireJS)和本机浏览器:
// Define UMD module for both AMD and browser. ((root, factory) => { // Detects AMD/RequireJS"s define function. if (typeof define === "function" && define.amd) { // Is AMD/RequireJS. Call factory with AMD/RequireJS"s define function. define("umdCounterModule", ["deependencyModule1", "dependencyModule2"], factory); } else { // Is Browser. Directly call factory. // Imported dependencies are global variables(properties of window object). // Exported module is also a global variable(property of window object) root.umdCounterModule = factory(root.deependencyModule1, root.dependencyModule2); } })(typeof self !== "undefined" ? self : this, (deependencyModule1, dependencyModule2) => { // Module code goes here. let count = 0; const increase = () => ++count; const reset = () => { count = 0; console.log("Count is reset."); }; return { increase, reset }; });
它比较复杂,但仍然只是 IIFE。匿名函数会检测是否存在 AMD 的 define
函数,若是存在,请使用 AMD 的define
函数调用模块工厂。若是不是,它将直接调用模块工厂。目前,root
参数其实是浏览器的 window
对象。它从全局变量( window
对象的属性)获取依赖项模块。当 factory
返回模块时,返回的模块也被分配给一个全局变量( window
对象的属性)。
如下是使模块定义与 AMD(RequireJS)和 CommonJS(Node.js)一块儿工做的另外一种 UMD 模式:
(define => define((require, exports, module) => { // Module code goes here. const dependencyModule1 = require("dependencyModule1"); const dependencyModule2 = require("dependencyModule2"); let count = 0; const increase = () => ++count; const reset = () => { count = 0; console.log("Count is reset."); }; module.export = { increase, reset }; }))(// Detects module variable and exports variable of CommonJS/Node.js. // Also detect the define function of AMD/RequireJS. typeof module === "object" && module.exports && typeof define !== "function" ? // Is CommonJS/Node.js. Manually create a define function. factory => module.exports = factory(require, exports, module) : // Is AMD/RequireJS. Directly use its define function. define);
别怕,这只是一个IIFE。调用IIFE时,将评估其参数。参数评估检测环境(CommonJS / Node.js的模块变量和exports
变量,以及 AMD/RequireJS 的 define
函数)。若是环境是 CommonJS/Node.js,则匿名函数的参数是手动建立的 define
函数。若是环境是 AMD/RequireJS,则匿名函数的参数就是 AMD 的 define
函数。所以,当执行匿名函数时,能够确保它具备有效的 define
函数。在匿名函数内部,它仅调用 define
函数来建立模块。
在全部模块混乱以后,JavaScript 的规范第 6 版在 2015 年定义了彻底不一样的模块系统和语法。该规范称为ECMAScript 2015 或 ES2015,AKA ECMAScript 6 或 ES6。主要语法是 import 关键字和 export 关键字。如下例子使用新语法演示 ES 模块的命名 import/export 和默认 import/export:
// Define ES module: esCounterModule.js or esCounterModule.mjs. import dependencyModule1 from "./dependencyModule1.mjs"; import dependencyModule2 from "./dependencyModule2.mjs"; let count = 0; // Named export: export const increase = () => ++count; export const reset = () => { count = 0; console.log("Count is reset."); }; // Or default export: export default { increase, reset };
要在浏览器中使用此模块文件,请添加 <script>
标签并指定它为模块:<script type="module" src="esCounterModule.js"></script>
。要在 Node.js 中使用此模块文件,请将其扩展名 .js
改成 .mjs
。
// Use ES module. // Browser: <script type="module" src="esCounterModule.js"></script> or inline. // Server: esCounterModule.mjs // Import from named export. import { increase, reset } from "./esCounterModule.mjs"; increase(); reset(); // Or import from default export: import esCounterModule from "./esCounterModule.mjs"; esCounterModule.increase(); esCounterModule.reset();
对于浏览器,能够将 <script>
的 nomodule
属性用于后备:
<script nomodule> alert("Not supported."); </script>
在 2020 年,最新的 JavaScript 规范第 11 版引入了内置函数 import
以动态使用 ES 模块。 import
函数返回一个 promise
,所以能够经过其 then
方法调用该模块:
// Use dynamic ES module with promise APIs, import from named export: import("./esCounterModule.js").then(({ increase, reset }) => { increase(); reset(); }); // Or import from default export: import("./esCounterModule.js").then(dynamicESCounterModule => { dynamicESCounterModule.increase(); dynamicESCounterModule.reset(); });
经过返回一个 promise
,显然 import 函数也能够与 await
关键字一块儿使用:
// Use dynamic ES module with async/await. (async () => { // Import from named export: const { increase, reset } = await import("./esCounterModule.js"); increase(); reset(); // Or import from default export: const dynamicESCounterModule = await import("./esCounterModule.js"); dynamicESCounterModule.increase(); dynamicESCounterModule.reset(); })();
如下是来自 https://developer.mozilla.org... 的导入、动态导入、导出的兼容性列表:
SystemJS 是一个库,能够为较旧的 ES5 启用 ES6 模块语法。例如如下模块以 ES6 语法定义:
// Define ES module. import dependencyModule1 from "./dependencyModule1.js"; import dependencyModule2 from "./dependencyModule2.js"; dependencyModule1.api1(); dependencyModule2.api2(); let count = 0; // Named export: export const increase = function () { return ++count }; export const reset = function () { count = 0; console.log("Count is reset."); }; // Or default export: export default { increase, reset }
若是当前运行时(例如旧的浏览器)不支持 ES6 语法,则以上代码将没法正常工做。 SystemJS 能够将模块定义转换为对库 API 的调用——System.register
:
// Define SystemJS module. System.register(["./dependencyModule1.js", "./dependencyModule2.js"], function (exports_1, context_1) { "use strict"; var dependencyModule1_js_1, dependencyModule2_js_1, count, increase, reset; var __moduleName = context_1 && context_1.id; return { setters: [ function (dependencyModule1_js_1_1) { dependencyModule1_js_1 = dependencyModule1_js_1_1; }, function (dependencyModule2_js_1_1) { dependencyModule2_js_1 = dependencyModule2_js_1_1; } ], execute: function () { dependencyModule1_js_1.default.api1(); dependencyModule2_js_1.default.api2(); count = 0; // Named export: exports_1("increase", increase = function () { return ++count }; exports_1("reset", reset = function () { count = 0; console.log("Count is reset."); };); // Or default export: exports_1("default", { increase, reset }); } }; });
这样新的 ES6 语法 import/export 就消失了。
SystemJS 还提供了用于动态导入的 import
函数:
// Use SystemJS module with promise APIs. System.import("./esCounterModule.js").then(dynamicESCounterModule => { dynamicESCounterModule.increase(); dynamicESCounterModule.reset(); });
Webpack 是模块的捆绑器。它使用将组合的 CommonJS 模块、AMD 模块和 ES 模块转换为和谐模块模式,并将全部代码捆绑到一个文件中。例如如下 3 个文件中用 3 种不一样的语法定义了 3 个模块:
// Define AMD module: amdDependencyModule1.js define("amdDependencyModule1", () => { const api1 = () => { }; return { api1 }; }); // Define CommonJS module: commonJSDependencyModule2.js const dependencyModule1 = require("./amdDependencyModule1"); const api2 = () => dependencyModule1.api1(); exports.api2 = api2; // Define ES module: esCounterModule.js. import dependencyModule1 from "./amdDependencyModule1"; import dependencyModule2 from "./commonJSDependencyModule2"; dependencyModule1.api1(); dependencyModule2.api2(); let count = 0; const increase = () => ++count; const reset = () => { count = 0; console.log("Count is reset."); }; export default { increase, reset }
如下文件使用了 counter 模块:
// Use ES module: index.js import counterModule from "./esCounterModule"; counterModule.increase(); counterModule.reset();
Webpack 能够将以上全部文件打包在一块儿,即便它们位于 3 个不一样的模块系统中,也都能打包为一个文件 main.js
:
root
dist
src
有趣的是,Webpack 自己使用 CommonJS 模块语法。在webpack.config.js
中:
const path = require('path'); module.exports = { entry: './src/index.js', mode: "none", // Do not optimize or minimize the code for readability. output: { filename: 'main.js', path: path.resolve(__dirname, 'dist'), }, };
如今运行如下命令以不一样的语法转换和捆绑 4 个文件:
npm install webpack webpack-cli --save-dev npx webpack --config webpack.config.js
从新格式化了如下捆绑文件 main.js
,并重命名了变量以提升可读性:
(function (modules) { // webpackBootstrap // The module cache var installedModules = {}; // The require function function require(moduleId) { // Check if module is in cache if (installedModules[moduleId]) { return installedModules[moduleId].exports; } // Create a new module (and put it into the cache) var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} }; // Execute the module function modules[moduleId].call(module.exports, module, module.exports, require); // Flag the module as loaded module.l = true; // Return the exports of the module return module.exports; } // expose the modules object (__webpack_modules__) require.m = modules; // expose the module cache require.c = installedModules; // define getter function for harmony exports require.d = function (exports, name, getter) { if (!require.o(exports, name)) { Object.defineProperty(exports, name, { enumerable: true, get: getter }); } }; // define __esModule on exports require.r = function (exports) { if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); } Object.defineProperty(exports, '__esModule', { value: true }); }; // create a fake namespace object // mode & 1: value is a module id, require it // mode & 2: merge all properties of value into the ns // mode & 4: return value when already ns object // mode & 8|1: behave like require require.t = function (value, mode) { if (mode & 1) value = require(value); if (mode & 8) return value; if ((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; var ns = Object.create(null); require.r(ns); Object.defineProperty(ns, 'default', { enumerable: true, value: value }); if (mode & 2 && typeof value != 'string') for (var key in value) require.d(ns, key, function (key) { return value[key]; }.bind(null, key)); return ns; }; // getDefaultExport function for compatibility with non-harmony modules require.n = function (module) { var getter = module && module.__esModule ? function getDefault() { return module['default']; } : function getModuleExports() { return module; }; require.d(getter, 'a', getter); return getter; }; // Object.prototype.hasOwnProperty.call require.o = function (object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; // __webpack_public_path__ require.p = ""; // Load entry module and return exports return require(require.s = 0); })([ function (module, exports, require) { "use strict"; require.r(exports); // Use ES module: index.js. var esCounterModule = require(1); esCounterModule["default"].increase(); esCounterModule["default"].reset(); }, function (module, exports, require) { "use strict"; require.r(exports); // Define ES module: esCounterModule.js. var amdDependencyModule1 = require.n(require(2)); var commonJSDependencyModule2 = require.n(require(3)); amdDependencyModule1.a.api1(); commonJSDependencyModule2.a.api2(); let count = 0; const increase = () => ++count; const reset = () => { count = 0; console.log("Count is reset."); }; exports["default"] = { increase, reset }; }, function (module, exports, require) { var result; !(result = (() => { // Define AMD module: amdDependencyModule1.js const api1 = () => { }; return { api1 }; }).call(exports, require, exports, module), result !== undefined && (module.exports = result)); }, function (module, exports, require) { // Define CommonJS module: commonJSDependencyModule2.js const dependencyModule1 = require(2); const api2 = () => dependencyModule1.api1(); exports.api2 = api2; } ]);
一样,它只是一个 IIFE。全部 4 个文件的代码都转换为 4 个函数中的代码。而且这 4 个函数做为参数传递给匿名函数。
Babel 是另外一个为旧版环境(如旧版浏览器)把 ES6 + JavaScript 代码转换为旧版语法的编译器。能够将 ES6 import/export 语法中的上述 counter 模块转换为如下替换了新语法的 babel 模块:
// Babel. Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } // Define ES module: esCounterModule.js. var dependencyModule1 = _interopRequireDefault(require("./amdDependencyModule1")); var dependencyModule2 = _interopRequireDefault(require("./commonJSDependencyModule2")); dependencyModule1["default"].api1(); dependencyModule2["default"].api2(); var count = 0; var increase = function () { return ++count; }; var reset = function () { count = 0; console.log("Count is reset."); }; exports["default"] = { increase: increase, reset: reset };
这是 index.js
中使用 counter
模块的代码:
// Babel. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } // Use ES module: index.js var esCounterModule = _interopRequireDefault(require("./esCounterModule.js")); esCounterModule["default"].increase(); esCounterModule["default"].reset();
这是默认的转译。 Babel 还能够与其余工具一块儿使用。
SystemJS 能够用做 Babel 的插件:
npm install --save-dev @babel/plugin-transform-modules-systemjs
并将其添加到 Babel 配置中:
{ "plugins": ["@babel/plugin-transform-modules-systemjs"], "presets": [ [ "@babel/env", { "targets": { "ie": "11" } } ] ] }
如今 Babel 能够与 SystemJS 一块儿使用以转换 CommonJS/Node.js 模块、AMD/RequireJS 模块和 ES 模块:
npx babel src --out-dir lib
结果是:
root
lib
src
如今全部 ADM、CommonJS 和 ES 模块语法都被转换为 SystemJS 语法:
// Transpile AMD/RequireJS module definition to SystemJS syntax: lib/amdDependencyModule1.js. System.register([], function (_export, _context) { "use strict"; return { setters: [], execute: function () { // Define AMD module: src/amdDependencyModule1.js define("amdDependencyModule1", () => { const api1 = () => { }; return { api1 }; }); } }; }); // Transpile CommonJS/Node.js module definition to SystemJS syntax: lib/commonJSDependencyModule2.js. System.register([], function (_export, _context) { "use strict"; var dependencyModule1, api2; return { setters: [], execute: function () { // Define CommonJS module: src/commonJSDependencyModule2.js dependencyModule1 = require("./amdDependencyModule1"); api2 = () => dependencyModule1.api1(); exports.api2 = api2; } }; }); // Transpile ES module definition to SystemJS syntax: lib/esCounterModule.js. System.register(["./amdDependencyModule1", "./commonJSDependencyModule2"], function (_export, _context) { "use strict"; var dependencyModule1, dependencyModule2, count, increase, reset; return { setters: [function (_amdDependencyModule) { dependencyModule1 = _amdDependencyModule.default; }, function (_commonJSDependencyModule) { dependencyModule2 = _commonJSDependencyModule.default; }], execute: function () { // Define ES module: src/esCounterModule.js. dependencyModule1.api1(); dependencyModule2.api2(); count = 0; increase = () => ++count; reset = () => { count = 0; console.log("Count is reset."); }; _export("default", { increase, reset }); } }; }); // Transpile ES module usage to SystemJS syntax: lib/index.js. System.register(["./esCounterModule"], function (_export, _context) { "use strict"; var esCounterModule; return { setters: [function (_esCounterModuleJs) { esCounterModule = _esCounterModuleJs.default; }], execute: function () { // Use ES module: src/index.js esCounterModule.increase(); esCounterModule.reset(); } }; });
TypeScript 支持 ES 模块语法(https://www.typescriptlang.or...),根据 tsconfig.json 中指定的 transpiler 选项,能够将其保留为 ES6 或转换为其余格式,包括 CommonJS/Node.js、AMD/RequireJS、UMD/UmdJS 或 System/SystemJS:
{ "compilerOptions": { "module": "ES2020", // None, CommonJS, AMD, System, UMD, ES6, ES2015, ES2020, ESNext. } }
例如:
// TypeScript and ES module. // With compilerOptions: { module: "ES6" }. Transpile to ES module with the same import/export syntax. import dependencyModule from "./dependencyModule"; dependencyModule.api(); let count = 0; export const increase = function () { return ++count }; // With compilerOptions: { module: "CommonJS" }. Transpile to CommonJS/Node.js module: var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; exports.__esModule = true; var dependencyModule_1 = __importDefault(require("./dependencyModule")); dependencyModule_1["default"].api(); var count = 0; exports.increase = function () { return ++count; }; // With compilerOptions: { module: "AMD" }. Transpile to AMD/RequireJS module: var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; define(["require", "exports", "./dependencyModule"], function (require, exports, dependencyModule_1) { "use strict"; exports.__esModule = true; dependencyModule_1 = __importDefault(dependencyModule_1); dependencyModule_1["default"].api(); var count = 0; exports.increase = function () { return ++count; }; }); // With compilerOptions: { module: "UMD" }. Transpile to UMD/UmdJS module: var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; (function (factory) { if (typeof module === "object" && typeof module.exports === "object") { var v = factory(require, exports); if (v !== undefined) module.exports = v; } else if (typeof define === "function" && define.amd) { define(["require", "exports", "./dependencyModule"], factory); } })(function (require, exports) { "use strict"; exports.__esModule = true; var dependencyModule_1 = __importDefault(require("./dependencyModule")); dependencyModule_1["default"].api(); var count = 0; exports.increase = function () { return ++count; }; }); // With compilerOptions: { module: "System" }. Transpile to System/SystemJS module: System.register(["./dependencyModule"], function (exports_1, context_1) { "use strict"; var dependencyModule_1, count, increase; var __moduleName = context_1 && context_1.id; return { setters: [ function (dependencyModule_1_1) { dependencyModule_1 = dependencyModule_1_1; } ], execute: function () { dependencyModule_1["default"].api(); count = 0; exports_1("increase", increase = function () { return ++count; }); } }; });
这在 TypeScript 中称为外部模块。
TypeScript还具备一个 module
关键字和一个 namespace
关键字。它们被称为内部模块:
module Counter { let count = 0; export const increase = () => ++count; export const reset = () => { count = 0; console.log("Count is reset."); }; } namespace Counter { let count = 0; export const increase = () => ++count; export const reset = () => { count = 0; console.log("Count is reset."); }; }
它们都被转换为 JavaScript 对象:
var Counter; (function (Counter) { var count = 0; Counter.increase = function () { return ++count; }; Counter.reset = function () { count = 0; console.log("Count is reset."); }; })(Counter || (Counter = {}));
经过支持 .
分隔符,TypeScript 模块和命名空间能够有多个级别:
module Counter.Sub { let count = 0; export const increase = () => ++count; } namespace Counter.Sub { let count = 0; export const increase = () => ++count; }
它们被转换为对象的属性:
var Counter; (function (Counter) { var Sub; (function (Sub) { var count = 0; Sub.increase = function () { return ++count; }; })(Sub = Counter.Sub || (Counter.Sub = {})); })(Counter|| (Counter = {}));
TypeScript 模块和命名空间也能够在 export
语句中使用:
module Counter { let count = 0; export module Sub { export const increase = () => ++count; } } module Counter { let count = 0; export namespace Sub { export const increase = () => ++count; } }
编译后 sub 模块和 sub 命名空间相同:
var Counter; (function (Counter) { var count = 0; var Sub; (function (Sub) { Sub.increase = function () { return ++count; }; })(Sub = Counter.Sub || (Counter.Sub = {})); })(Counter || (Counter = {}));
欢迎使用 JavaScript,它具备如此丰富的功能——仅用于模块化/命名空间的就有 10 多种系统和格式:
幸运的是,如今 JavaScript 有模块的标准内置语言功能,而且 Node.js 和全部最新的现代浏览器都支持它。对于较旧的环境,你仍然能够用新的 ES 模块语法进行编码,而后用 Webpack/Babel/SystemJS/TypeScript 转换为较旧或兼容的语法。