ES6提供了兩個關鍵字import
導入和export
導出,語法上有些差異。!important:
import
和export
必須,始終顯示在,其各自用法的,頂級範圍中。也就是說不能夠在if
條件中放置import
或export
; 它們必須放在全部塊
和函數
以外。函數
exporting
API Members 導出API成員export
關鍵字要麼放在聲明以前
,要麼做爲運算符
(排序)使用要export
的特定綁定列表
。性能
export function foo() { // .. } export var awesome = 42; var bar = [1,2,3]; export { bar };
相同的表達exports
的方法:優化
function foo() { // .. } var awesome = 42; var bar = [1,2,3]; export { foo, awesome, bar };
這些都稱爲 命名導出( named export)
,由於您實際上導出變量/函數/等
的名稱綁定
。
任何你不標記export
,會在模塊的範圍內保持private
。 也就是說,儘管var bar = ..
看起來像是在頂級全局範圍
聲明,頂級範圍
其實是模塊
自己; 模塊中沒有全局範圍
。設計
注意:模塊仍然能夠訪問window
和全部的globals
,掛起它,只是否是做爲詞彙頂級範圍。 可是,你真的應該遠離全局模塊,若是可能的話。指針
您還能夠在命名導出期間對模塊成員進行「重命名」(也稱爲別名):code
function foo() { .. } export { foo as bar };
導入此模塊時,只有bar
成員名稱可用於導入; foo
保持隱藏在模塊內。對象
模塊導出不只僅是值或引用的正常賦值,由於您習慣於=
賦值運算符。 實際上,當你導出一些東西,你正在導出一個綁定(有點像一個指針)到那個東西(變量等)。排序
在模塊中,若是更改已導出綁定的變量的值,即便已經導入(參見下一部分),導入的綁定也將解析爲當前(更新)的值。作用域
試想一下:開發
var awesome = 42; export { awesome }; // later awesome = 100;
導入此模塊時,不管是在awesome = 100
設置以前仍是以後,一旦分配已經發生,導入的綁定將解析爲值100
,而不是42
。
這是由於綁定本質上是對awesome
變量自己的引用或指針,而不是其值的副本。JS引入了ES6模塊綁定,這是一個大多數史無前例的概念。
雖然您能夠在模塊的定義中清楚地屢次使用export
,但ES6
絕對偏好模塊,具備單個導出的方法,這稱爲default export
。用TC39
委員會的一些成員的話來講,若是你遵循這種模式,你就會被「簡單的import語法」
所獎勵,而若是你不這麼作,則反過來被更冗長的語法來「懲罰」。
默認導出
將特定導出的綁定設置爲導入模塊時的默認值。綁定的名稱字面上是默認的。正如你將在後面看到的,當導入模塊綁定時,你也能夠重命名它們,就像你一般使用默認導出同樣。
每一個模塊定義只能有一個默認值。若是模塊具備默認導出
,您將看到導入語法如何更簡潔。
對於默認導出
語法有一個微妙的細微差異,您應該密切注意。比較這兩個片斷:
function foo(..) { // .. } export default foo;
and
function foo(..) { // .. } export { foo as default };
在第一個片斷中,您正在導出一個綁定到此時的函數表達式值,而不是標識符foo
。 換句話說,導出export default ..
採用一個表達式。 若是之後將foo
分配給模塊中的其餘值,則模塊導入仍會顯示最初導出的函數,而不是新值。
順便說一下,第一個片斷也能夠寫成:
export default function foo(..) { // .. }
警告:雖然這裏function foo..
在技術上,是一個函數表達式,爲了模塊的內部範圍的目的,它被視爲一個函數聲明,由於foo
名稱綁定在模塊的頂級做用域 稱爲「提高」)。 出口默認類Foo
也是如此。可是,雖然你能夠作到export var foo = ..,
但你目前不能作到export default var foo = ..(或let或const)
,在一個使人沮喪的不一致的狀況下。 在撰寫本文時,爲了一致性,已經討論了在ES6以後很快增長這種能力。
function foo(..) { // .. } export { foo as default };
在此版本的模塊導出中,默認導出綁定其實是foo
標識符而不是它的值
,因此您得到前面描述的綁定行爲(即,若是您之後更改foo
的值,你在import
端看到的值也將更新)。
在默認導出語法中要很是當心這個微妙的問題,特別是若是你的邏輯須要更新導出值。 若是你歷來沒有計劃更新默認導出的值,export default ..
是很好的。 若是您計劃更新值,則必須使用export {.. as default}
。 不管哪一種方式,確保評論你的代碼如何來解釋你的意圖!
由於每一個模塊只能有一個默認值,因此您可能會試圖使用一個對象的默認導出,來設計您的模塊,其中包含全部的API
方法,例如:
export default { foo() { .. }, bar() { .. }, .. };
這種模式,彷佛很密切地,映射了不少開發人員,已經構建了,他們的ES6以前的模塊,因此,它彷佛是,一個天然的方法。 不幸的是,它存在一些缺點,並正式地不鼓勵這樣作。
特別是,JS引擎,不能靜態分析普通對象的內容,這意味着,它不能對靜態導入性能,作一些優化。 每一個成員單獨和顯式導出的優勢是,引擎能夠進行靜態分析和優化。
若是你的API,已經有多個成員,彷佛這些原則 - 每一個模塊一個默認導出,全部API成員名爲exports - 存在衝突,不是嗎? 可是您能夠有一個默認導出以及其餘命名導出; 它們不是相互排斥的。
所以,替換掉當前的(不建議的)模式:
export default function foo() { .. } foo.bar = function() { .. }; foo.baz = function() { .. };
you can do that
export default function foo() { .. } export function bar() { .. } export function baz() { .. }
注意:在前面的代碼段中,我使用名稱foo
做爲默認函數的標籤。 然而,名稱foo
,爲了導出的目的被忽略 - default
其實是導出的名稱。 當您導入此默認綁定時,您能夠爲其指定任何所需的名稱,以下一部分中所示。
function foo() { .. } function bar() { .. } function baz() { .. } export { foo as default, bar, baz, .. };
當咱們短時間覆蓋import
時,混合default
和命名exports
的影響將更加清晰。但實質上它意味着最簡潔的默認import
形式,將只檢索foo()
函數。用戶能夠另外手動將bar
和baz
列爲命名導入,若是他們想要的話。
你能夠想象若是你有不少命名的導出綁定,你模塊的用戶將是多麼乏味。這裏有一個通配符導入表單,您能夠在單個命名空間對象中導入全部模塊的exports
,可是沒法將通配符導入到頂級綁定。
一樣,ES6模塊機制被有意設計爲阻止具備大量輸出的模塊;相對來講,但願這樣的方法有點困難,做爲一種社會工程,它鼓勵簡單的模塊設計,由於這將有利於大/複雜的模塊設計。
我建議你不要混合default export
與命名exports
,這將是是不切實際或不須要,特別是若是你有一個大的API和重構單獨的模塊。在這種狀況下,只需使用全部named exports,並記錄您的模塊的用戶應該使用import * as ..
(命名空間導入)方法在單個命名空間中當即帶來整個API。
咱們以前提到過,但讓咱們更詳細地回味一下。除了導出表達式值綁定的export default ...
外,全部其餘導出表單,都正在導出綁定到本地標識符。