<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 24.模塊 > 原文: [http://exploringjs.com/impatient-js/ch_modules.html](http://exploringjs.com/impatient-js/ch_modules.html) JavaScript 模塊的當前環境非常多樣化:ES6 帶來了內置模塊,但是它們之前的模塊系統仍然存在。了解后者有助于理解前者,所以讓我們進行調查。 ### 24.1。在模塊之前:腳本 最初,瀏覽器只有 _ 腳本 _ - 在全局范圍內執行的代碼片段。例如,考慮一個 HTML 文件,它通過以下 HTML 元素加載 _ 腳本文件 _: ```html <script src="my-library.js"></script> ``` 在腳本文件中,我們模擬一個模塊: ```js var myModule = function () { // Open IIFE // Imports (via global variables) var importedFunc1 = otherLibrary1.importedFunc1; var importedFunc2 = otherLibrary2.importedFunc2; // Body function internalFunc() { // ··· } function exportedFunc() { importedFunc1(); importedFunc2(); internalFunc(); } // Exports (assigned to global variable `myModule`) return { exportedFunc: exportedFunc, }; }(); // Close IIFE ``` 在我們開始使用實際模塊(在 ES6 中引入)之前,所有代碼都是用 ES5 編寫的(沒有`const`和`let`,只有`var`)。 `myModule`是一個全局變量。定義模塊的代碼包含在 _ 立即調用的函數表達式 _(IIFE)中。創建一個函數并立即調用它,與直接執行代碼相比只有一個好處(不包裝它):在 IIFE 中定義的所有變量都保持在其范圍內,不會變為全局變量。最后,我們選擇要導出的內容并通過對象字面值返回。這種模式被稱為 _ 揭示模塊模式 _(由 Christian Heilmann 創造)。 這種模擬模塊的方法有幾個問題: * 腳本文件中的庫通過全局變量導出和導入功能,這會冒名稱沖突的風險。 * 沒有明確聲明依賴關系,并且腳本沒有內置的方法來加載它所依賴的腳本。因此,網頁不僅要加載頁面所需的腳本,還要加載這些腳本的依賴關系,依賴項的依賴關系等等。它必須按正確的順序執行! ### 24.2。在 ES6 之前創建的模塊系統 在 ECMAScript 6 之前,JavaScript 沒有內置模塊。因此,該語言的靈活語法用于在語言中實現自定義模塊系統 _。兩個流行的是 CommonJS(針對服務器端)和 AMD(異步模塊定義,針對客戶端)。_ #### 24.2.1。服務器端:CommonJS 模塊 模塊的原始 CommonJS 標準主要是為服務器和桌面平臺創建的。它是 Node.js 模塊系統的基礎,在那里它獲得了令人難以置信的流行度。對這種受歡迎程度的貢獻是 Node 的軟件包管理器 npm,以及支持在客戶端使用 Node 模塊(browserify 和 webpack)的工具。 從現在開始,我可以互換地使用術語 _CommonJS 模塊 _ 和 _Node.js 模塊 _,即使 Node.js 還有一些額外的功能。以下是 Node.js 模塊的示例。 ```js // Imports var importedFunc1 = require('other-module1').importedFunc1; var importedFunc2 = require('other-module2').importedFunc2; // Body function internalFunc() { // ··· } function exportedFunc() { importedFunc1(); importedFunc2(); internalFunc(); } // Exports module.exports = { exportedFunc: exportedFunc, }; ``` CommonJS 的特征如下: * 專為服務器設計。 * 模塊意味著同步加載。 * 緊湊的語法。 #### 24.2.2。客戶端:AMD(異步模塊定義)模塊 創建 AMD 模塊格式是為了在瀏覽器中比 CommonJS 格式更容易使用。它最受歡迎的實現是 RequireJS。以下是 RequireJS 模塊的示例。 ```js define(['other-module1', 'other-module2'], function (otherModule1, otherModule2) { var importedFunc1 = otherModule1.importedFunc1; var importedFunc2 = otherModule2.importedFunc2; function internalFunc() { // ··· } function exportedFunc() { importedFunc1(); importedFunc2(); internalFunc(); } return { exportedFunc: exportedFunc, }; }); ``` AMD 的特點如下: * 專為瀏覽器設計。 * 模塊意味著異步加載。這對于瀏覽器來說是一個至關重要的要求,代碼不能等到模塊下載完畢。必須在模塊可用時通知它。 * 語法稍微復雜一些。從好的方面來說,AMD 模塊可以直接執行,無需自定義創建和執行源代碼(想想`eval()`)。網上并不總是允許這樣做。 #### 24.2.3。 JavaScript 模塊的特征 看看 CommonJS 和 AMD,JavaScript 模塊系統之間的相似之處出現了: * 每個文件有一個模塊(AND 每個文件也支持多個模塊)。 * 這樣的文件基本上是一段執行的代碼: * 導出:該代碼包含聲明(變量,函數等)。默認情況下,這些聲明保留在模塊的本地,但您可以將其中一些聲明標記為導出。 * 導入:模塊可以從其他模塊導入實體。那些其他模塊通過 _ 模塊說明符 _(通常是路徑,偶爾 URL)來識別。 * 模塊是 _ 單例 _:即使多次導入模塊,也只存在單個實例。 * 沒有使用全局變量。相反,模塊說明符用作全局 ID。 ### 24.3。 ECMAScript 模塊 ESAS 引入了 ECMAScript 模塊:它們堅定地遵循 JavaScript 模塊的傳統,并分享現有模塊系統的許多特性: * 使用 CommonJS,ES 模塊共享緊湊語法,單個導出的語法比 _ 命名導出 _(到目前為止,我們只看到命名導出)和支持循環依賴關系更好。 * 對于 AMD,ES 模塊共享異步加載和可配置模塊加載的設計(例如,如何解析說明符)。 ES 模塊也有新的好處: * 它們的語法比 CommonJS 更緊湊。 * 它們的模塊具有靜態結構(在運行時無法更改)。這樣可以實現靜態檢查,優化的導入訪問,更好的捆綁(交付更少的代碼)等等。 * 他們對循環進口的支持是完全透明的。 這是 ES 模塊語法的示例: ```js import {importedFunc1} from 'other-module1'; import {importedFunc2} from 'other-module2'; function internalFunc() { ··· } export function exportedFunc() { importedFunc1(); importedFunc2(); internalFunc(); } ``` 從現在開始,“模塊”意味著“ECMAScript 模塊”。 #### 24.3.1。 ECMAScript 模塊:三部分 ECMAScript 模塊包括三個部分: 1. 聲明模塊語法:什么是模塊?如何申報進出口? 2. 語法的語義:如何處理由導入創建的變量綁定?如何處理導出的變量綁定? 3. 用于配置模塊加載的編程加載器 API。 第 1 部分和第 2 部分與 ES6 一起介紹。第 3 部分的工作正在進行中。 ### 24.4。命名出口 每個模塊可以有零個或多個命名導出。 例如,請考慮以下三個文件: ```js lib/my-math.js main1.js main2.js ``` 模塊`my-math.js`有兩個命名導出:`square`和`MY_CONSTANT`。 ```js let notExported = 'abc'; export function square(x) { return x * x; } export const MY_CONSTANT = 123; ``` 模塊`main1.js`有一個命名導入,`square`: ```js import {square} from './lib/my-math.js'; assert.equal(square(3), 9); ``` 模塊`main2.js`有一個所謂的 _ 命名空間導入 _ - `my-math.js`的所有命名導出都可以作為對象`myMath`的屬性訪問: ```js import * as myMath from './lib/my-math.js'; assert.equal(myMath.square(3), 9); ``` ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:命名出口** `exercises/modules/export_named_test.js` ### 24.5。默認導出 每個模塊最多只能有一個默認導出。這個想法是模塊 _ 是 _ 的默認導出值。模塊可以同時具有命名導出和默認導出,但通常最好堅持每個模塊一種導出樣式。 作為默認導出的示例,請考慮以下兩個文件: ```js my-func.js main.js ``` 模塊`my-func.js`具有默認導出: ```js export default function () { return 'Hello!'; } ``` 模塊`main.js`默認 - 導入導出的函數: ```js import myFunc from './my-func.js'; assert.equal(myFunc(), 'Hello!'); ``` 注意語法差異:命名導入周圍的花括號表示我們將 _ 傳入 _ 模塊,而默認導入 _ 是 _ 模塊。 默認導出的最常見用例是包含單個函數或單個類的模塊。 #### 24.5.1。默認導出的兩種樣式 執行默認導出有兩種樣式。 首先,您可以使用`export default`標記現有聲明: ```js export default function foo() {} // no semicolon! export default class Bar {} // no semicolon! ``` 其次,您可以直接默認導出值。在那種風格中,`export default`本身就像一個宣言。 ```js export default 'abc'; export default foo(); export default /^xyz$/; export default 5 * 7; export default { no: false, yes: true }; ``` 為什么有兩種默認導出樣式?原因是`export default`不能用于標記`const`:`const`可能定義多個值,但`export default`只需要一個值。 ```js // Not legal JavaScript! export default const foo = 1, bar = 2, baz = 3; ``` 使用此假設代碼,您不知道三個值中的哪一個是默認導出。 ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:默認導出** `exercises/modules/export_default_test.js` ### 24.6。命名模塊 命名模塊文件及其導入的變量沒有既定的最佳實踐。 在本章中,我使用了以下命名方式: * 模塊文件的名稱是破折號的,并以小寫字母開頭: ```js ./my-module.js ./some-func.js ``` * 命名空間導入的名稱是小寫的和駝峰式的: ```js import * as myModule from './my-module.js'; ``` * 默認導入的名稱是小寫的和駝峰式的: ```js import someFunc from './some-func.js'; ``` 這種風格背后的理由是什么? * npm 不允許包名中的大寫字母( [source](npm%20doesn’t%20allow%20uppercase%20letters) )。因此,我們避免使用駝峰,因此“本地”文件的名稱與 npm 包的名稱一致。 * 將基于短劃線的文件名轉換為以駝峰為基礎的 JavaScript 變量名稱有明確的規則。由于我們如何命名命名空間導入,這些規則適用于命名空間導入和默認導入。 我也喜歡下劃線模塊文件名,因為你可以直接使用這些名稱進行名稱空間導入(沒有任何翻譯): ```js import someFunc from './some-func.js'; ``` 但是這種樣式對默認導入不起作用:我喜歡下劃線外殼用于命名空間對象,但它不是函數等的好選擇。 ### 24.7。導入是導出的只讀視圖 到目前為止,我們直觀地使用了進口和出口,一切似乎都按預期運作。但現在是時候仔細研究進出口的真實關系了。 考慮以下兩個模塊: ```js counter.js main.js ``` `counter.js`導出一個(mutable!)變量和一個函數: ```js export let counter = 3; export function incCounter() { counter++; } ``` `main.js` name-導入兩個導出。當我們使用`incCounter()`時,我們發現與`counter`的連接是實時的 - 我們總是可以訪問該變量的實時狀態: ```js import { counter, incCounter } from './counter.js'; // The imported value `counter` is live assert.equal(counter, 3); incCounter(); assert.equal(counter, 4); ``` 請注意,雖然連接是實時的并且我們可以讀取`counter`,但我們無法更改此變量(例如,通過`counter++`)。 為什么 ES 模塊會以這種方式運行? 首先,分割模塊更容易,因為以前的共享變量可以成為導出。 其次,這種行為對于循環導入至關重要。在執行模塊之前,模塊的導出是已知的。因此,如果模塊 L 和模塊 M 相互導入,則循環地執行以下步驟: * L 的執行開始。 * L 進口 M. L's 進口指向 M 內的未初始化槽。 * L'的尸體尚未執行。 * M 的執行開始(由導入觸發)。 * M 進口 L. * M 的主體被執行。現在 L's 進口有值(由于實時連接)。 * L 的主體被執行。現在 M 的進口有值。 循環導入是您應該盡可能避免的,但它們可能出現在復雜系統或重構系統中。重要的是,當發生這種情況時,事情不會破裂。 ### 24.8。模塊說明符 一個關鍵規則是: > 所有 ES 模塊說明符必須是有效的 URL 并指向實際文件。 除此之外,一切仍然有點不穩定。 #### 24.8.1。模塊說明符的類別 在我們進一步了解之前,我們需要建立以下類別的模塊說明符(源自 CommonJS): * 相對路徑:以點開頭。例子: ```js './some/other/module.js' '../../lib/counter.js' ``` * 絕對路徑:以斜杠開頭。例: ```js '/home/jane/file-tools.js' ``` * 完整的 URL:包括協議(從技術上講,路徑也是 URL)。例: ```js 'https://example.com/some-module.js' ``` * 裸路徑:不要以點,斜線或協議開頭。在 CommonJS 模塊中,裸路徑很少有文件擴展名。 ```js 'lodash' 'mylib/string-tools' 'foo/dist/bar.js' ``` #### 24.8.2。 Node.js 中的 ES 模塊說明符 Node.js 中對 ES 模塊的支持正在進行中。目前的計劃(截至 2018-12-20)是按如下方式處理模塊說明符: * 相對路徑,絕對路徑和完整 URL 按預期工作。他們都必須指向真實的文件。 * 裸路徑: * 內置模塊(`path`,`fs`等)可以通過裸路徑導入。 * 所有其他裸路徑必須指向文件:`'foo/dist/bar.js'` * ES 模塊的默認文件擴展名為`.mjs`(可能有一種方法可以切換到每個包的不同擴展名)。 #### 24.8.3。瀏覽器中的 ES 模塊說明符 瀏覽器處理模塊說明符如下: * 相對路徑,絕對路徑和完整 URL 按預期工作。他們都必須指向真實的文件。 * 最終如何處理裸路徑尚不清楚。您最終可以通過查找表將它們映射到其他說明符。 * 模塊的文件擴展名無關緊要,只要它們與內容類型`text/javascript`一起提供即可。 請注意,將模塊說明符編譯為單個文件的瀏覽器和 webpack 等捆綁工具對模塊說明符的限制要少于瀏覽器,因為它們在編譯時運行,而不是在運行時運行。 ### 24.9。語法缺陷:導入不是解構 導入和解構看起來都很相似: ```js import {foo} from './bar.js'; // import const {foo} = require('./bar.js'); // destructuring ``` 但它們完全不同: * 進口與出口保持聯系。 * 您可以在解構模式中再次進行解構,但導入語句中的`{}`不能嵌套。 * 重命名的語法不同: ```js import {foo as f} from './bar.js'; // importing const {foo: f} = require('./bar.js'); // destructuring ``` 理由:解構讓人想起對象字面值(包括嵌套),而導入則喚起重命名的想法。 ### 24.10。預覽:動態加載模塊 到目前為止,導入模塊的唯一方法是通過`import`語句。這些語句的局限性: * 您必須在模塊的頂層使用它們。也就是說,當你在一個街區內時,你不能導入一些東西。 * 模塊說明符始終是固定的。也就是說,您無法根據條件更改導入的內容,也無法動態檢索或組裝說明符。 [即將推出的 JavaScript 功能](https://github.com/tc39/proposal-dynamic-import)改變了:`import()`運算符,它被用作異步函數(它只是一個運算符,因為它需要隱式訪問當前模塊的 URL)。 請考慮以下文件: ```js lib/my-math.js main1.js main2.js ``` 我們已經看過模塊`my-math.js`: ```js let notExported = 'abc'; export function square(x) { return x * x; } export const MY_CONSTANT = 123; ``` 這是在`main1.js`中使用`import()`的樣子: ```js const dir = './lib/'; const moduleSpecifier = dir + 'my-math.js'; function loadConstant() { return import(moduleSpecifier) .then(myMath => { const result = myMath.MY_CONSTANT; assert.equal(result, 123); return result; }); } ``` 方法`.then()`是 _Promises_ 的一部分,這是一種處理異步結果的機制,本書稍后將對此進行介紹。 此代碼中的兩件事在以前是不可能的: * 我們在函數內部導入(不在頂層)。 * 模塊說明符來自變量。 接下來,我們將在`main2.js`中實現完全相同的功能,但是通過所謂的 _ 異步函數 _,它為 Promises 提供了更好的語法。 ```js const dir = './lib/'; const moduleSpecifier = dir + 'my-math.js'; async function loadConstant() { const myMath = await import(moduleSpecifier); const result = myMath.MY_CONSTANT; assert.equal(result, 123); return result; } ``` 唉,`import()`還不是 JavaScript 的標準版本,但可能會相對較快。這意味著支持是混合的,可能不一致。 ### 24.11。進一步閱讀 * 更多關于`import()`:[“ES 提案:`import()` - 在 2ality 上動態導入 ES 模塊”](http://2ality.com/2017/01/import-operator.html)。 * 有關 ECMAScript 模塊的深入了解,請參考[“探索 ES6”](http://exploringjs.com/es6/ch_modules.html)。 ![](https://img.kancloud.cn/ff/a8/ffa8e16628cad59b09c786b836722faa.svg) **測驗** 參見[測驗應用程序](ch_quizzes-exercises.html#quizzes)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看