<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>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                > 原文出處:http://www.infoq.com/cn/articles/es6-in-depth-modules 早在2007年我剛加入Mozilla的JavaScript團隊的時候廣為流傳一個笑話:通常來說JavaScript程序的長度只有一行。 那時候Google Maps的誕齡還只有兩歲,在它誕生之前,JavaScript主要被用來驗證表單,毫無疑問,每一個處`理<input onchange=>`的程序真的就只有一行代碼。 今 非昔比,JavaScript項目的規模已經成長到令人驚嘆的地步,社區也開發了很多工具來協助構建這些規模龐大的應用。諸多工具中最核心的非模塊系統莫 屬,在這樣一個系統中,你可以通過多個文件和目錄來組織你的項目,最重要的是,所有代碼都可以按需彼此訪問并高效加載。所以JavaScript就這么順 理成章地擁也了幾個知名的模塊系統。當然,隨之而來的還有幾個包管理器,可以用它們安裝所有軟件以及處理高層次的依賴。如此一來,你一定會覺得ES6的模 塊語法真是姍姍來遲啊。 好的,那今天我們就一起看看ES6中新增的模塊系統,探討一下我們可以通過新語法打造什么樣的工具并進一步推動未來標準的發展。但是首先,我們還是來了解一下ES6模塊的基礎知識。 ## 模塊基礎知識 每一個ES6模塊都是一個包含JS代碼的文件,模塊本質上就是一段腳本,而不是用`module`關鍵字定義一個模塊,但是模塊與腳本還是有兩點區別: * 在ES6模塊中,無論你是否加入“`use strict;`”語句,默認情況下模塊都是在嚴格模式下運行。 * 在模塊中你可以使`用import`和`export`關鍵字。 我們先來討論`export`。默認情況下,你在模塊中的所有聲明相對于模塊而言都是寄存在本地的。如果你希望公開在模塊中聲明的內容,并讓其它模塊加以使用,你一定要導出這些功能。想要導出模塊的功能有很多方法,其中最簡單的方式是添加`export`關鍵字。 ~~~ // kittydar.js - 找到一幅圖像中所有貓的位置 // (事實上是Heather Arthur寫的這個庫) // (但是她沒有使用ES6中新的模塊特性,因為那時候是2013年) export function detectCats(canvas, options) { var kittydar = new Kittydar(options); return kittydar.detectCats(canvas); } export class Kittydar { ... 處理圖片的幾種方法 ... } // 這個helper函數沒有被export。 function resizeCanvas() { ... } ... ~~~ 你可以`導出`所有的最外層`函數`、`類`以及`var`、`let`或`const`聲明的變量。 了解這些,你就可以編寫一個簡單的模塊。你不需要將所有代碼都放在一個[IIFE](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression)或回調中,你只需要在模塊中解放手腳,聲明你需要的所有內容。代碼就是模塊,不是一段腳本,所以所有的聲明都被限定在模塊的作用域中,對所有腳本和模塊全局不可見。你需要做的是將組成模塊公共API的聲明全部導出。 在模塊中,除export之外的代碼無異于普通代碼,你可以訪問類似`Object`和`Array`這樣的全局對象。如果你在web瀏覽器中運行模塊,你甚至可以使用`document`對象和`XMLHttpRequest`對象。 在一個獨立文件中,我們可以導入`detectCats()`函數然后用它來做點兒什么: ~~~ // demo.js - Kittydar的demo程序 import {detectCats} from "kittydar.js"; function go() { var canvas = document.getElementById("catpix"); var cats = detectCats(canvas); drawRectangles(canvas, cats); } ~~~ 如果想從一個模塊中導入多個名稱,你可以這樣寫: ~~~ import {detectCats, Kittydar} from "kittydar.js"; ~~~ 當你運行的模塊中包含一條`import`聲明時,首先會加載被導入的模塊;然后依賴圖的深度優先遍歷按順序執行每一個模塊的主體代碼;為了避免形成回環,所有已執行的模塊都會被忽略。 這些就是模塊的基本知識了,相當簡單吧。;-) ## Export列表 你不需要標記每一個被導出的特性,你只需要在花括號中按照列表的格式寫下你想導出的所有名稱: ~~~ export {detectCats, Kittydar}; // 此處不需要 `export`關鍵字 function detectCats(canvas, options) { ... } class Kittydar { ... } ~~~ `export`列表可以在模塊文件最外層作用域的每一處聲明,不一定非要把它放在模塊文件的首行。你也可以聲明多個`export`列表,甚至通過其它的`export`聲明打造一個混合的`export`列表,只要保證每一個被導出的名稱是唯一的即可。 ## 重命名import和export 恰恰有時候,導出的名稱會與你需要使用的其它名稱產生沖突,ES6為你提供了重命名的方法解決這個問題,當你在導入名稱時可以這樣做: ~~~ // suburbia.js // 這兩個模塊都會導出以`flip`命名的東西。 // 要同時導入兩者,我們至少要將其中一個的名稱改掉。 import {flip as flipOmelet} from "eggs.js"; import {flip as flipHouse} from "real-estate.js"; ... ~~~ 同樣,當你在導出的時候也可以重命名。你可能會想用兩個不同的名稱導出相同的值,這樣的情況偶爾也會遇到: ~~~ // unlicensed_nuclear_accelerator.js - 無DRM(數字版權管理)的媒體流 // (這不是一個真實存在的庫,但是或許它應該被做成一個庫) function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion }; ~~~ ## Default exports 現在廣泛使用的模塊系統有CommonJS、AMD兩種,設計出來的新標準可以與這兩種模塊進行交互。所以假設你有一個Node項目,你已經執行了`npm install lodash`,你的ES6模塊可以從Lodash中導入獨立的函數: ~~~ import {each, map} from "lodash"; each([3, 2, 1], x => console.log(x)); ~~~ 但是也許你已經習慣看到`_.each`的書寫方式而不想直接用`each`函數呢?或者你就真的想導入整個`_`函數呢,畢竟[_對于Lodash而言至關重要](https://lodash.com/docs#_)。 針對這種情況,你可以換用一種稍微不太一樣的方法:不用花括號來導入模塊。 ~~~ import _ from "lodash"; ~~~ 這種簡略的表達方法等價于`import {default as _} from "lodash";`。在ES6的模塊中導入的CommonJS模塊和AMD模塊都有一個`默認的`導出,如果你用`require()`加載這些模塊也會得到相同的結果——`exports`對象。 ES6模塊不只導出CommonJS模塊,它的設計邏輯為你提供導出不同內容的多種方法,默認導出的是你得到的所有內容。舉個例子,在用這種寫法的時候,據我所知,著名的[colors](https://github.com/Marak/colors.js)包就沒有任何針對ES6的支持。像大多數npm上的包一樣,它是諸多CommonJS模塊的集合,但是你可以正確地將它導入到你的ES6代碼中。 ~~~ // `var colors = require("colors/safe");`的ES6等效代碼 import colors from "colors/safe"; ~~~ 如果你想讓自己的ES6模塊有一個默認的導出,實現的方法很簡單,默認導出與其它類型的導出相似,沒有什么技巧可言,唯一的不同之處是它被命名為“`default`”。你可以用我們剛才討論的重命名語法來實現: ~~~ let myObject = { field1: value1, field2: value2 }; export {myObject as default}; ~~~ 這種簡略的表達方法看起來更清爽: ~~~ export default { field1: value1, field2: value2 }; ~~~ 關鍵字`export default`后可跟隨任何值:一個函數、一個類、一個對象字面量,只要你能想到的都可以。 ## 模塊對象 很抱歉新特性有點兒多,但JavaScript不是唯一這樣做的語言:出于某些原因,每一種語言中的模塊系統都有這么一堆又獨立又小,雖然無聊但是很方便的特性。不過還好,我們只剩一樣東西沒講了。好吧,是兩樣。 ~~~ import * as cows from "cows"; ~~~ 當你`import *`時,導入的其實是一個模塊命名空間對象,模塊將它的所有屬性都導出了。所以如果“cows”模塊導出一個名為`moon()`的函數,然后用上面這種方法“cows”將其全部導入后,你就可以這樣調用函數了:`cows.moo()`。 ## 聚合模塊 有時一個程序包中主模塊的代碼比較多,為了簡化這樣的代碼,可以用一種統一的方式將其它模塊中的內容聚合在一起導出,可以通過這種簡單的方式將所有所需內容導入再導出: ~~~ // world-foods.js - 來自世界各地的好東西 // 導入"sri-lanka"并將它導出的內容的一部分重新導出 export {Tea, Cinnamon} from "sri-lanka"; // 導入"equatorial-guinea"并將它導出的內容的一部分重新導出 export {Coffee, Cocoa} from "equatorial-guinea"; // 導入"singapore"并將它導出的內容全部導出 export * from "singapore"; ~~~ 這些`export-from`語句每一個都好比是在一條`import-from`語句后伴隨著一個`export`。與真正的導入內容的方法不同的是,這些導入內容再重新導出的方法不會在作用域中綁定你導入的內容。如果你打算用`world-foods.js`中的`Tea`來寫一些代碼,可別用這種方法導入模塊,你會發現當前模塊作用域中根本找不到`Tea`。 如果從“singapore”導出的任何名稱碰巧與其它的導出沖突了,可能會觸發一個錯誤,所以使用`export *`語句的時候要格外小心。 呼!終于講完了所有的語法!現在來講一些有趣的內容。 ## import實際都做了些什么? 如果我說它什么都沒做,你敢信? 哦,看來你沒那么容易上當啊。好吧,你相信標準里面通常都不會規定`import`的行為么?如果真是這樣,那這是件好事兒么? ES6將模塊加載過程的細節完全[交由最終的實現來定義](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hostresolveimportedmodule),模塊執行的其它部分倒是[在規范中有詳細定義](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-toplevelmoduleevaluationjob)。 粗略地講,當你通知JS引擎運行一個模塊時,它一定會按照以下四個步驟執行下去: 1. 語法解析:閱讀模塊源代碼,檢查語法錯誤。 2. 加載:遞歸地加載所有被導入的模塊。這也正是沒被標準化的部分。 3. 連接:每遇到一個新加載的模塊,為其創建作用域并將模塊內聲明的所有綁定填充到該作用域中,其中包括由其它模塊導入的內容。 4. 如果你的代碼中有`import {cake} from "paleo"`這樣的語句,而此時“paleo”模塊并沒有導出任何“`cake`”,你就會觸發一個錯誤。這實在是太糟糕了,你都快要運行模塊中的代碼了,都是cake惹的禍! 5. 運行時:最終,在每一個新加載的模塊體內執行所有語句。此時,導入的過程就已經結束了,所以當執行到達有一行`import`聲明的代碼的時候……什么都沒發生! 看到了嘛?我可告訴過你結果是“啥都沒有”哦。事關編程語言我絕不撒謊! 但是現在我們真的要深入了解這個系統最有趣的部分了!有一個很酷的小技巧我可以教給你。系統不指定加載過程的實現方式,你也可以通過在源代碼中查找`import`聲明提前計算出所有依賴,你可以將ES6系統實現為:在編譯時計算所有依賴并將所有模塊打包成一個文件,通過網絡一次傳輸所有模塊!像[webpack](http://www.2ality.com/2015/04/webpack-es6.html)這樣的工具就實現了這個功能。 這種做法的意義非常深遠,因為通過網絡加載腳本需要花費時間,每當你請求到一個模塊,你可能發現它里面也包含著`import`聲 明,這就需要你再花費一些時間加載更多的腳本。基于如此天真的思想實現的加載器需要消耗更多的網絡往返時間。但是webpack就不一樣啦,它所用的加載 器是經過精心設計的,吸收了軟件工程領域的精華,所以你不僅可以立即開始使用ES6模塊系統,還不會損耗運行時的性能。 最初的時候,標準委員會已經制定并實現了詳細的ES6模塊加載標準,它未成為最終的標準的原因是成員們沒有就代碼封包(bundle)功能的實現方式達成一 致意見。我希望有人能搞定這個問題,正如我們所見,模塊加載的過程亟待被標準化;最關鍵的是,封包的功能實在是太好,就這樣放棄對其進行標準化有些可惜 啊。 ## 靜態vs動態:論規則及破例之法 JavaScript作為一門動態語言已經得到了一個令人驚訝的靜態模塊系統。 * 你只可以在模塊的最外層作用域使用`import`和`export`,不可在條件語句中使用,也不能在函數作用域中使用`import`。 * 所有導出的標識符一定要在源代碼中明確地導出它們的名稱,你不能通過編寫代碼遍歷一個數組然后用數據驅動的方式導出一堆名稱。 * 模塊對象被凍結了,所以你無法hack模塊對象并為其添加polyfill風格的新特性。 * 一個模塊的所有依賴必須在模塊代碼運行前完全加載、解析并且及早連接,不存在一種通過`import`來按需懶加載的語法。 * `import`模塊產生的錯誤沒有錯誤恢復機制。一個app可能囊括了上百個模塊,一旦有一個模塊無法加載或連接,所有的模塊都不會運行,而且你不能在`try/catch`代碼塊中捕捉`import`的錯誤信息。(上面這些描述的本意是說:系統是靜態的,webpack可在編譯時為你檢測那些錯誤。) * 不支持在模塊加載依賴前運行其它代碼的鉤子,這也意味著無法控制模塊的依賴加載過程。 只要你的需求是靜態的,系統就會運行良好,但是你有時可以設想下需要一點兒hack,對么? 這也就是無論你用什么模塊加載系統,你都將有一個編程API來支持ES6的靜態`import/export`語法。舉個例子,[webpack中引入了一個“代碼分割”API](http://webpack.github.io/docs/code-splitting.html),從而可以按需懶加載一些模塊的多個封包。相同的API可以幫你打破上面列舉的絕大多數其它的規則。 ES6模塊語法非常靜態,這是很好的——它通過強有力的編譯時工具的形式進行彌補。但是設計靜態語法的初衷是要與豐富的動態編程加載器API一起增強ES6的模塊系統。 ## 我什么時候可以使用ES6模塊? 如果你現在就想在項目中加入新的模塊語法,你需要使用[Babel](http://babeljs.io/)或[Traceur](https://github.com/google/traceur-compiler#what-is-traceur)這樣的轉譯器。在系列之前的文章中,[Gastón I. Silva展示了如何使用Babel和Broccoli](http://www.infoq.com/cn/articles/es6-in-depth-babel-and-broccoli)來為web平臺編譯ES6代碼;在那篇文章的基礎上,Gastón準備了[一個支持ES6模塊的工作示例](https://github.com/givanse/broccoli-babel-examples/tree/master/es6-modules)。[Axel Rauschmayer寫的這篇文章](http://www.2ality.com/2015/04/webpack-es6.html)給出了一個用Babel和webpack構建項目的示例。 ES6模塊系統主要由Dave Herman和Sam Tobin-Hochstadt進行設計,在近幾年的爭論中,他們與所有參與者(包括我)為新模塊系統的靜態部分進行辯護。Jon Coppeard負責在Firefox中實現這些模塊的特性。[JavaScript加載器標準](https://github.com/whatwg/loader)也在制定當中,接下來標準委員會可能會為HTML添加一些類似`<script type=module>`特性。 然后這就是ES6的全部啦。 深入淺出ES6非常有趣,我可不想匆匆結束。可能我們應該再多寫一期文章,討論一下ES6規范犄角旮旯里的特性,這些東西不太重要,甚至連標準委員會自己都不愿意寫文章來說說這些特性。我可能再討論一下JavaScript未來的導向,下一次請記得回來圍觀深入淺出ES6的驚天大結局!
                  <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>

                              哎呀哎呀视频在线观看