做者: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 轉換爲較舊或兼容的語法。