ES6 Module之export 解讀

已默認讀者了解本篇自言自語的context,且對于module有所了解,對于module的相關擴展說明將穿插在內容中(本篇不會提及class),由于是只是export,所以......import請容我忽略

本篇代碼運行環境為{"presets": [ "es2015","stage-2" ] }這只是作為參考,且只是運行環境,不推薦在學習ES6時將代碼全部轉譯為ES5,ES6轉化后的代碼只能告訴你結果,相比較而言,原因或是理由的價值超過結果,學習ES6,不單單要知道代碼運行的結果,最重要的目的是,知其所以然,了解一個行為為什么會這么發生,行為的背后又是什么,這才是學習者所需要追尋的

文章參考:
You-Dont-Know-JS
ECMAScript 6 入門
ES6規范15.2.3.2 Static Semantics: BoundNames

首先登場的就是 export , 這是一個主要關鍵詞,基本用法是放置在一個"聲明"之前,或一組由{}語法(注意,此處的{}語法與對象無關)包裹的即將被導出的"標識符"之前

//export 放置在"聲明"之前
export var a = 1, b = 2, c = 3
export let a = 1
export const a = 1
export let { a } = { a: 1 }
export var foo = function() {}
export function foo() {}

//export 放置在一組"標識符"之前
var a = 1, b = 2
export { a, b }
//等同于
export var a = 1
export var b = 2

以上例子有一個明確的共同點,export 后面沒有出現“表達式”。實際上,單獨的export 是對變量標識符(指針位置)的綁定,并期許將來會把對應的標識符(指針)導出。

將“變量標識符”導出,這樣的描述容易產生混淆,考慮下面的代碼

var a = 1
export { a }
a = 3
//等同于
export var a = 1
a = 3

當這個模塊被導出后,如果賦值發生,那么已被導出的值也將被更新,無論導出發生在任何階段。進一步的說,在被導入時的值是無關緊要的。這些綁定是實時的鏈接,所以唯一重要的是當你訪問這個綁定時它當前的值是什么。

  1. 聲明
  2. 導出標識符 a,此刻的 a 是指向變量本身的一個引用,或指針,而不是它的值的一個拷貝
  3. 模塊內部,a 被重新賦值,已經導出的值也會被自動更新
參考規范15.2.3.2
ExportDeclaration : export VariableStatement
  1. Return the BoundNames of VariableStatement.
ExportDeclaration : export Declaration
  1. Return the BoundNames of Declaration.
可以看到export 的導出是明確的

另外“標識符”一詞引用于The above rule means that each ReferencedBindings of ExportClause is treated as an IdentifierReference.主要是詞窮,并且因為模塊導出與賦值是不同的,在進行導出時,實質上是導出了一個單向綁定的(不允許在導入的一方進行改變)變量的引用,是的,更準確的說應該是對于變量這個容器的引用,模塊導出并不關心變量的值。

在進行導出的時候,可以使用別名,關鍵詞 as

var a = 1
export { a as b }
//將 a 重命名為 b

"在一個模塊中,使用import命令的時候,用戶需要知道所要加載的變量名或函數名,否則無法加載。但是,用戶肯定希望快速上手,未必愿意閱讀文檔,去了解模塊有哪些屬性和方法。"

"為了給用戶提供方便,讓他們不用閱讀文檔就能加載模塊,就要用到export default命令,為模塊指定默認輸出。"

上述兩句直接摘自ECMAScript 6 入門,主要是感覺是很恰當的描述,如果加以變動反倒畫蛇添足了,當然還不夠全面,所以對default進來以下的補充

  1. 每個模塊定義只能有一個default,它是唯一的,每個被導出的模塊只包含一個default元素,所以export default命令在模塊內只被允許使用一次。

  2. 本質上export default就是輸出一個叫做default的默認標識符。等同于,將export default 之后的內容以賦值的形式添加到default元素上。

var a = 1
export default a 
//等同于
export default 1
//導出的是那表達式在那一刻的值的綁定,不是 標識符a的綁定(export.default = 表達式)

export default 有許多微妙的細節,令人困擾的(不是結果,而是行為)。請思考下面的代碼。

//m2.js
1.
function foo() {}
export default foo
foo = 'change'

2.
export default (function foo() {})
foo = 'change'

3.
function foo() {}
export { foo as default }
foo = 'change'
//由于上文已經描述過,default接近于標識符,所以,可以直接重命名foo作為default導出。

4.
export default function foo() {}
foo = 'change'

//另一模塊
import * as all from 'm2.js'
console.log(all)

你的大腦能夠清晰的知道每個模塊即將會發生的事情嗎?如果不能那么請繼續閱讀,如果能,那么也希望你繼續閱讀,重溫復習這一片段。下面讓我復制代碼,描述并解釋每一模塊的行為。

模塊1.
function foo() {}
//聲明foo
export default foo
//將foo賦值給default元素(注意,此時foo是表達式)
foo = 'change'

//結果
{ default: [Function: foo] }

export default 導出的是那一個函數表達式在那一刻的值的綁定,不是 標識符foo的綁定。換句話說,export default ..接收一個表達式。如果你稍后在你的模塊內部賦給foo一個不同的值,這個模塊導入將依然表示原本被導出的函數,而不是那個新的值。

規范里定義了export default 表達式的導出相關行為export default AssignmentExpression

ExportDeclaration : export default AssignmentExpression ;
1.Return ?"default"?.
簡單解釋下,就是將表達式的值賦予default,然后返回default

模塊2.
export default (function foo() {})
將(..)賦值給default 元素(注意,()是表達式)
foo = 'change'
//結果
ReferenceError: foo is not defined
export default !function foo() {}
!等運算符可以包裝一個函數使它作為一個表達式返回值

export default (function foo(){}),export default后面的是函數表達式,并不是函數聲明定義,所以它對應的規范與模塊1相同,導出的ExportedBindings也就是一個?"default"?。

這里之所以報錯是因為函數表達式只會返回函數本身作為值,并不會在外部作用域定義同名變量,所以下面的foo = 'change'找不到foo這個定義。

模塊3.
function foo() {}
export { foo as default }
foo = 'change'
//結果
{ default: 'change' }

ExportDeclaration : export Declaration
1.Return the BoundNames of Declaration.
行為與 export '標識符‘相同,所以引用的規范相同,唯一需要理解的是default是可以被賦值

模塊4.
export default function foo() {}
//一個函數聲明出現了!
foo = 'change'
//結果
{ default: 'change' }

function foo..部分是一個函數表達式,但是對于模塊內部作用域來說,它被視為一個函數聲明,因為名稱foo被綁定在模塊的頂層作用域

export default 函數聲明定義在規范中定義的行為,對應的是export default HoistableDeclaration.
ExportDeclaration : export default HoistableDeclaration
1.Let declarationNames be the BoundNames of HoistableDeclaration.
2.If declarationNames does not include the element "default", append "default" to declarationNames.
3.Return declarationNames.

可以看到按照規范如果將要導出的聲明沒有包含元素default,那么就進行賦值(規范概念),最后返回的是一個當前綁定的標識符,與前面的表達式時狀態不同。所以結果能夠看到,導出值被更新了。

原標題為ES6 Module 詳解,然后發現自己并沒有真正理解Module這部分于是停筆,回去看書了,實際意義上本文是為了總結知識

初學ES6,文章有誤請指點,文內部分用詞不準確也請諒解,雖然引用了規范,但是并沒有能力進行解讀,慚愧。

文章參考:
You-Dont-Know-JS
ECMAScript 6 入門
ES6規范15.2.3.2 Static Semantics: BoundNames

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 官方中文版原文鏈接 感謝社區中各位的大力支持,譯者再次奉上一點點福利:阿里云產品券,享受所有官網優惠,并抽取幸運大...
    HetfieldJoe閱讀 3,668評論 2 27
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • 以下內容是我在學習和研究ES6時,對ES6的特性、重點和注意事項的提取、精練和總結,可以做為ES6特性的字典;在本...
    科研者閱讀 3,150評論 2 9
  • 總是有很多話不去說 總是有很多事不去想 總是認為自己所做的一切都是對的 總是不敢回頭面對殷切的暮光 繁華的城市從沒...
    擇若閱讀 473評論 4 1