摘要:你可能認(rèn)為和它的新模塊系統(tǒng)出現(xiàn)得有點(diǎn)晚。聚合模塊有時(shí)候一個(gè)包的主模塊只不過(guò)是導(dǎo)入包其他所有的模塊,并用統(tǒng)一的方式導(dǎo)出。靜態(tài)動(dòng)態(tài),或者說(shuō)規(guī)則如何打破規(guī)則作為一個(gè)動(dòng)態(tài)編譯語(yǔ)言,令人驚奇的是擁有一個(gè)靜態(tài)的模塊系統(tǒng)。
回想2007年,那時(shí)候我剛加入Mozilla"s JavaScript團(tuán)隊(duì),那時(shí)候的一個(gè)典型的JavaScript程序只需要一行代碼,聽(tīng)起來(lái)像個(gè)笑話。
兩年后,Google Maps發(fā)布。在這之前,JavaScript主要用來(lái)做表單的驗(yàn)證,你用來(lái)處理這個(gè)程序當(dāng)然只需要一行。
時(shí)過(guò)境遷,JavaScript項(xiàng)目已經(jīng)發(fā)展到讓人嘆為觀止,社區(qū)涌現(xiàn)了許多幫助開(kāi)發(fā)的工具。但是最迫切需要的是一個(gè)模塊系統(tǒng),它能將你的工作分散到不同的文件與目錄中,在需要的時(shí)候他們能彼此之間相互訪問(wèn),并且可以有效的加載所有代碼。所以JavaScript有模塊系統(tǒng)這很正常,而且還有多個(gè)模塊系統(tǒng)(CommonJS、AMD、CMD、UMD)。不僅如此,它還有幾個(gè)包管理器(npm、bower),用來(lái)安裝軟件還能拷貝一些深度依賴(lài)。你可能認(rèn)為ES6和它的新模塊系統(tǒng)出現(xiàn)得有點(diǎn)晚。
那我們來(lái)看看ES6為現(xiàn)存的模塊系統(tǒng)添加了什么,以及未來(lái)的標(biāo)準(zhǔn)和工具能否建立在這個(gè)系統(tǒng)上。首先,讓我們看看ES6的模塊是什么樣子的。
模塊的基礎(chǔ)知識(shí)ES6模塊是一個(gè)包含了JS代碼的文件。沒(méi)有所謂的module關(guān)鍵詞,一個(gè)模塊看起來(lái)和一個(gè)腳本文件沒(méi)什么不一樣,除了一下兩個(gè)區(qū)別:
ES6的模塊自動(dòng)開(kāi)啟嚴(yán)格模式,即使你沒(méi)有寫(xiě)"use strict";;
在模塊中,你可以使用import和exprot。
先來(lái)談?wù)別xport。在默認(rèn)情況下,模塊中所有的聲明都是私有的,如果你希望模塊中的某些聲明是公開(kāi)的,并在其他模塊中使用它們,你就必須導(dǎo)出它們。這里有一些實(shí)現(xiàn)方法,最簡(jiǎn)單的是添加export關(guān)鍵字
// kittydar.js - Find the locations of all the cats in an image. // (Heather Arthur wrote this library for real) // (but she didn"t use modules, because it was 2013) export function detectCats(canvas, options) { var kittydar = new Kittydar(options); return kittydar.detectCats(canvas); } export class Kittydar { ... several methods doing image processing ... } // This helper function isn"t exported. function resizeCanvas() { ... } ...
你可以export任何的頂級(jí)變量:function、class、var、let、const。
你如果要寫(xiě)一個(gè)模塊知道這么多就夠了!你不必再把所有的東西放到一個(gè)立即執(zhí)行函數(shù)或者回調(diào)函數(shù)里面,只需要在你需要的地方進(jìn)行聲明。由于這個(gè)代碼是一個(gè)模塊,而不是一個(gè)腳本,所有的聲明的作用域都只屬于這個(gè)模塊,而不是所有腳本和模塊都能全局訪問(wèn)的。你只要把模塊中的聲明導(dǎo)出成一組公共模塊的API就足夠了。
除了導(dǎo)出,模塊里的代碼和其他普通代碼沒(méi)有什么區(qū)別。它可以訪問(wèn)全局變量,像Object和Array。如果你的模塊在瀏覽器運(yùn)行,還能夠使用document和XMLHttpRequest。
在另一個(gè)文件中,我們可以導(dǎo)入并使用detectCats()函數(shù):
// demo.js - Kittydar demo program import {detectCats} from "kittydar.js"; function go() { var canvas = document.getElementById("catpix"); var cats = detectCats(canvas); drawRectangles(canvas, cats); }
要從一個(gè)模塊導(dǎo)入多個(gè)變量,你可以這樣寫(xiě):
import {detectCats, Kittydar} from "kittydar.js";
當(dāng)你運(yùn)行一個(gè)包含import聲明的模塊,會(huì)先導(dǎo)入要導(dǎo)入的模塊并加載,然后根據(jù)深度優(yōu)先的原則遍歷依賴(lài)圖譜來(lái)執(zhí)行對(duì)應(yīng)模塊,并跳過(guò)已經(jīng)執(zhí)行的模塊,來(lái)避免循環(huán)。
這就是模塊基礎(chǔ)知識(shí),這真的很簡(jiǎn)單。;-)
導(dǎo)出列表你可以把你要導(dǎo)出的功能名寫(xiě)在一個(gè)列表里,然后用大括號(hào)括起來(lái),這樣就不用在每個(gè)要導(dǎo)出的功能前面加上export標(biāo)記。
export {detectCats, Kittydar}; // no `export` keyword required here function detectCats(canvas, options) { ... } class Kittydar { ... }
導(dǎo)出列表并不需要寫(xiě)在文件的第一行,它可以出現(xiàn)在模塊文件的頂級(jí)作用域的任何位置。你可以有多個(gè)導(dǎo)出列表,或者將導(dǎo)出列表與導(dǎo)出聲明混合使用,只要不重復(fù)導(dǎo)出同一個(gè)變量名就行。
重命名導(dǎo)出和導(dǎo)入有時(shí),導(dǎo)入的變量名碰巧與你需要使用的一些變量名沖突了,ES6允許你重命名導(dǎo)入的變量。
// suburbia.js // Both these modules export something named `flip`. // To import them both, we must rename at least one. import {flip as flipOmelet} from "eggs.js"; import {flip as flipHouse} from "real-estate.js"; ...
同樣,你在導(dǎo)出變量的時(shí)候也可以重命名它們。這在你想使用不同名字導(dǎo)出相同功能的時(shí)候十分方便。
// unlicensed_nuclear_accelerator.js - media streaming without drm // (not a real library, but maybe it should be) function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion };默認(rèn)的導(dǎo)出
新的標(biāo)準(zhǔn)在設(shè)計(jì)上是兼容已經(jīng)存在的CommonJS和AMD模塊的。如果你有一個(gè)Node項(xiàng)目,并且你已經(jīng)執(zhí)行了npm install lodash。你使用ES6代碼能夠多帶帶引入Lodash中的函數(shù):
import {each, map} from "lodash"; each([3, 2, 1], x => console.log(x));
如果你已經(jīng)習(xí)慣使用_.each而不是each,你依然想像以前一樣使用它?;蛘?, 你想把_當(dāng)成一個(gè)函數(shù)來(lái)使用,因?yàn)檫@才是Lodash。
這種情況下,你只要稍微改變下你的寫(xiě)法:不使用花括號(hào)來(lái)導(dǎo)入模塊。
import _ from "lodash";
這個(gè)寫(xiě)法等同于 import {default as _} from "lodash";。所有的CommonJS 和AMD模塊在ES6中都能被當(dāng)作default導(dǎo)出,這個(gè)導(dǎo)出和你在CommonJS中使用require()導(dǎo)出得到東西一樣,即exports對(duì)象。
ES6模塊在設(shè)計(jì)上可以讓你導(dǎo)出更多的東西,但對(duì)于現(xiàn)在的CommonJS模塊,導(dǎo)出的default模塊就是能導(dǎo)出的全部東西了。例如,在寫(xiě)這篇文章時(shí),據(jù)我所知,著名的colors模塊沒(méi)有特意去支持ES6語(yǔ)法,這是一個(gè)CommonJS模塊組成的包,就像npm上的那些包一樣,但是你可以直接引入到你的ES6代碼中。
// ES6 equivalent of `var colors = require("colors/safe");` import colors from "colors/safe";
如果你希望自己ES6模塊也具有默認(rèn)導(dǎo)出,這很簡(jiǎn)單。默認(rèn)的導(dǎo)出方式并沒(méi)有什么魔力;他就像其他導(dǎo)出一樣,除了它的導(dǎo)出名為default。你可以使用我們之前提到的重命名語(yǔ)法:
let myObject = { field1: value1, field2: value2 }; export {myObject as default};
或者使用簡(jiǎn)寫(xiě):
export default { field1: value1, field2: value2 };
export default關(guān)鍵詞后面可以跟任何值:一個(gè)函數(shù)、一個(gè)類(lèi)、一個(gè)對(duì)象,所有能被命名的變量。
模塊對(duì)象不好意思,這篇文章有點(diǎn)長(zhǎng)。JavaScript并不孤獨(dú):因?yàn)橐恍┰?,所有的語(yǔ)言中都有模塊系統(tǒng),并且傾向于設(shè)計(jì)大量雜亂而又無(wú)聊的小特性。幸運(yùn)的是我們只剩下一個(gè)話題,噢,不對(duì),是兩個(gè)。
import * as cows from "cows";
當(dāng)你使用import *的時(shí)候,被引入的是一個(gè)模塊命名空間對(duì)象(module namespace object),它的屬性是模塊的輸出。如果“cows”模塊導(dǎo)出一個(gè)名為moo()的函數(shù),那么在導(dǎo)入“cows”之后,你可以使用cows.moo()來(lái)進(jìn)行調(diào)用。
聚合模塊有時(shí)候一個(gè)包的主模塊只不過(guò)是導(dǎo)入包其他所有的模塊,并用統(tǒng)一的方式導(dǎo)出。為了簡(jiǎn)化這種代碼,有一種將導(dǎo)入導(dǎo)出全部合一的簡(jiǎn)寫(xiě):
// world-foods.js - good stuff from all over // import "sri-lanka" and re-export some of its exports export {Tea, Cinnamon} from "sri-lanka"; // import "equatorial-guinea" and re-export some of its exports export {Coffee, Cocoa} from "equatorial-guinea"; // import "singapore" and export ALL of its exports export * from "singapore";
這種export-from表達(dá)式類(lèi)似于import-from后面跟了一個(gè)export。這和真正的導(dǎo)入有一些區(qū)別,它不會(huì)在當(dāng)前作用域中綁定將要導(dǎo)出的變量。如果你打算在world-foods.js中使用Tea來(lái)編寫(xiě)一些代碼,請(qǐng)不要使用這種簡(jiǎn)寫(xiě),你會(huì)發(fā)現(xiàn)Tea為定義。
如果“singapore”導(dǎo)出的命名與其他導(dǎo)出發(fā)生了沖突,那就會(huì)出現(xiàn)錯(cuò)誤,所以請(qǐng)謹(jǐn)慎使用。
呼,我們已經(jīng)把語(yǔ)法介紹完了!下面來(lái)談?wù)勔恍┯腥さ氖虑椤?/p> import到底做了什么?
不管你信不信,它什么都沒(méi)做。
噢,你看起來(lái)沒(méi)那么好騙。那么你會(huì)相信標(biāo)準(zhǔn)幾乎沒(méi)有說(shuō)import到底該怎么做嗎?這是件好事嗎?(作者貌似很愛(ài)開(kāi)玩笑。)
ES6將模塊的加載細(xì)節(jié)完全交給了實(shí)現(xiàn),其余的模塊執(zhí)行部分卻規(guī)定得非常詳細(xì)。
簡(jiǎn)單來(lái)說(shuō),當(dāng)你告訴JS引擎運(yùn)行一個(gè)模塊的時(shí)候,它的行為可以歸納為以下四部:
解析:讀取模塊的源代碼,并檢查語(yǔ)法錯(cuò)誤。
加載:加載所有的導(dǎo)入模塊(遞歸進(jìn)行),這是還未標(biāo)準(zhǔn)化的部分。
鏈接:對(duì)于每個(gè)新加載的模塊,在實(shí)現(xiàn)上都會(huì)創(chuàng)建一個(gè)作用域,并把模塊中聲明的所有變量都綁定在這個(gè)作用域上,包括從其他模塊導(dǎo)入的變量。
如果你想試試import {cake} from "paleo",但是“paleo”模塊沒(méi)真正導(dǎo)出名為cake的變量,你會(huì)得到一個(gè)錯(cuò)誤。這很糟糕,因?yàn)槟汶x運(yùn)行js并品嘗蛋糕只有一步之遙。
運(yùn)行時(shí)間:最后,開(kāi)始執(zhí)行加載進(jìn)來(lái)的新的模塊中的代碼。這時(shí),整個(gè)import過(guò)程已經(jīng)完成了,所以前面說(shuō)代碼執(zhí)行到import這一行聲明時(shí),什么都沒(méi)有發(fā)生。
看到?jīng)]?我說(shuō)了什么都不會(huì)發(fā)生,在編程語(yǔ)言這件事上,我從來(lái)都不說(shuō)慌。
現(xiàn)在我們可以開(kāi)始介紹這個(gè)系統(tǒng)中有趣的部分了。這有一個(gè)非常炫酷的技巧。由于系統(tǒng)沒(méi)有指定如何加載的這方面的細(xì)節(jié),并且你可以通過(guò)查看源代碼中導(dǎo)入的聲明,提前計(jì)算出所有的依賴(lài)項(xiàng),所以ES6的實(shí)現(xiàn)可以通過(guò)預(yù)處理器完成所有的工作,然后把所有的模塊打包到一個(gè)文件中,最后通過(guò)網(wǎng)絡(luò)進(jìn)行請(qǐng)求一次即可。像webpack這樣的工具就是這么做的。
這是一個(gè)優(yōu)雅的解決方案,因?yàn)橥ㄟ^(guò)網(wǎng)絡(luò)加載所有的腳本文件很耗時(shí),假如你請(qǐng)求一個(gè)資源后,發(fā)現(xiàn)里面有import聲明,然后你又得請(qǐng)求更多資源。一個(gè)加載器需要非常多的網(wǎng)絡(luò)請(qǐng)求來(lái)回傳輸。通過(guò)webpack,你不僅能在今天就使用ES6的模塊話,你還能獲得很多好處,并且不需要擔(dān)心會(huì)造成運(yùn)行時(shí)的性能下降。
原本是有計(jì)劃制定一個(gè)ES6中模塊加載的詳細(xì)規(guī)范的,并且已經(jīng)初步成型。它沒(méi)有成為標(biāo)準(zhǔn)的原因之一是不知道如何與打包這一特性進(jìn)行整合。我希望模塊化的加載會(huì)更加標(biāo)準(zhǔn)化,也希望打包工具會(huì)越來(lái)越好。
靜態(tài) VS 動(dòng)態(tài),或者說(shuō):規(guī)則如何打破規(guī)則作為一個(gè)動(dòng)態(tài)編譯語(yǔ)言,令人驚奇的是JavaScript擁有一個(gè)靜態(tài)的模塊系統(tǒng)。
所有的import和export只能寫(xiě)在頂級(jí)作用域中。你不能在條件判斷語(yǔ)句和函數(shù)作用域內(nèi)使用import。
所有導(dǎo)出的變量名必須是顯式的,你不能通過(guò)遍歷一個(gè)數(shù)組,動(dòng)態(tài)生成一組導(dǎo)出名進(jìn)行導(dǎo)出。
模塊對(duì)象都是被凍結(jié)的,不能通過(guò)polyfill為它添加新的特性。
在所有模塊運(yùn)行之前, 其依賴(lài)的模塊都必須經(jīng)過(guò)加載、解析、鏈接的過(guò)程,目前沒(méi)有import懶加載相關(guān)的語(yǔ)法。(現(xiàn)在import()方法已經(jīng)在提案中了)
對(duì)于import的錯(cuò)誤,無(wú)法進(jìn)行recovery。一個(gè)應(yīng)用可能依賴(lài)許多的模塊,一旦有一個(gè)模塊加載失敗,這個(gè)應(yīng)用都不會(huì)運(yùn)行。你不能在try/catch中使用import。正是因?yàn)閑s6的模塊表現(xiàn)得如此靜態(tài),webpack才能在編譯的時(shí)候檢測(cè)出代碼中的錯(cuò)誤。
你沒(méi)法為一個(gè)模塊在加載所有依賴(lài)項(xiàng)之前添加鉤子,這意味著一個(gè)模塊沒(méi)有辦法控制其依賴(lài)項(xiàng)的加載方式。
如果你的需求是靜態(tài)的,ES6的模塊系統(tǒng)還是相當(dāng)不錯(cuò)的。但是你有時(shí)候你還是向進(jìn)行一些hack,對(duì)吧?
這就是為什么你使用的模塊加載系統(tǒng)會(huì)提供一些系統(tǒng)層次的API來(lái)配合ES6的靜態(tài)的import/export語(yǔ)法。例如,webpack有一個(gè)API能進(jìn)行代碼的分割,按照你的需求對(duì)一些模塊進(jìn)行懶加載。這個(gè)API能夠打破之前列出的規(guī)矩。
ES6的模塊語(yǔ)法非常靜態(tài),這很好-在使用一些編譯工具時(shí)我們都能?chē)L到一些甜頭。
靜態(tài)語(yǔ)法的設(shè)計(jì)可以讓它與動(dòng)態(tài)加載器豐富的API進(jìn)行工作。
如果你今天就想使用,你需要一個(gè)預(yù)編譯器,如 Traceur 和 Babel 。這個(gè)系列之前也有相關(guān)文章,Gastón I. Silva:如何使用 Babel 和 Broccoli 編譯 ES6 代碼為 web 可用。Gastón也將案例放在了 GitHub 上。另外這篇文章也介紹了如何使用 Babel 和 webpack。
ES6 模塊系統(tǒng)由 Dave Herman 和 Sam Tobin-Hochstadt進(jìn)行設(shè)計(jì),他們不顧多人(包括我)的反對(duì),多年來(lái)始終堅(jiān)持模塊系統(tǒng)是靜態(tài)的。JonCoppeard正在Firefox上實(shí)現(xiàn)ES6的模塊化功能。JavaScript Loader的相關(guān)標(biāo)準(zhǔn)的工作也正在進(jìn)行中,預(yù)計(jì)在HTML中將會(huì)被添加類(lèi)似 這樣的東西。
這便是 ES6 了。
這太有趣了,我不希望現(xiàn)在就結(jié)束。也許我們還能再說(shuō)一會(huì)。我們還能夠討論一些關(guān)于ES6規(guī)范中零零碎碎的東西,但這些又不足夠?qū)懗晌恼?。也行?huì)有一些關(guān)于ES6未來(lái)特性的一些東西,盡請(qǐng)期待下周的ES6 In Depth
原文鏈接:ES6 In Depth: Modules
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/89739.html
摘要:的翻譯文檔由的維護(hù)很多人說(shuō),阮老師已經(jīng)有一本關(guān)于的書(shū)了入門(mén),覺(jué)得看看這本書(shū)就足夠了。前端的異步解決方案之和異步編程模式在前端開(kāi)發(fā)過(guò)程中,顯得越來(lái)越重要。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(shū)(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書(shū)的目的是以目前還在制定中的ECMASc...
摘要:同時(shí),迭代器有一個(gè)方法來(lái)向函數(shù)中暫停處拋出一個(gè)錯(cuò)誤,該錯(cuò)誤依然可以通過(guò)函數(shù)內(nèi)部的模塊進(jìn)行捕獲處理。 本文翻譯自:Diving Deeper With ES6 Generators 由于個(gè)人能力有限,翻譯中難免有紕漏和錯(cuò)誤,望不吝指正issue ES6 Generators:完整系列 The Basics Of ES6 Generators Diving Deeper With E...
摘要:從最開(kāi)始的到封裝后的都在試圖解決異步編程過(guò)程中的問(wèn)題。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。寫(xiě)一個(gè)符合規(guī)范并可配合使用的寫(xiě)一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來(lái)處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問(wèn)題描述 在開(kāi)發(fā)過(guò)程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過(guò)http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過(guò)...
摘要:前端日?qǐng)?bào)精選變量聲明與賦值值傳遞淺拷貝與深拷貝詳解淺談自適應(yīng)學(xué)習(xí)比你想象的要簡(jiǎn)單常見(jiàn)排序算法之實(shí)現(xiàn)世界萬(wàn)物誕生記中文深入理解筆記與異步編程譯不可變和中的知乎專(zhuān)欄譯怎樣避免開(kāi)發(fā)時(shí)的深坑瘋狂的技術(shù)宅在翻譯網(wǎng)格布局掘金詳解改變模糊度亮 2017-08-15 前端日?qǐng)?bào) 精選 ES6 變量聲明與賦值:值傳遞、淺拷貝與深拷貝詳解淺談web自適應(yīng)學(xué)習(xí) React.js 比你想象的要簡(jiǎn)單常見(jiàn)排序算法之...
摘要:前端日?qǐng)?bào)精選百分比實(shí)現(xiàn)比例固定圖片自適應(yīng)布局探索中的架構(gòu)你不知道的總結(jié)中譯深入解析中種發(fā)起請(qǐng)求的方法帶著三個(gè)問(wèn)題一起深入淺出高階組件中文深入理解筆記用模塊封裝代碼第期中的匿名遞歸是如何防御攻擊的眾成翻譯中的響應(yīng)編程掘金,新特性搶先 2017-08-17 前端日?qǐng)?bào) 精選 CSS百分比padding實(shí)現(xiàn)比例固定圖片自適應(yīng)布局探索ReactJS中的CSS架構(gòu)你不知道的JavaScript-總...
閱讀 2933·2021-11-23 09:51
閱讀 1587·2021-11-15 11:36
閱讀 3047·2021-10-13 09:40
閱讀 2022·2021-09-28 09:35
閱讀 13172·2021-09-22 15:00
閱讀 1398·2019-08-29 13:56
閱讀 2955·2019-08-29 13:04
閱讀 2725·2019-08-28 18:06