[譯]ES6 模塊化入門

ES6 模塊系統

在 ES6 以前,咱們已經知道了 RequireJS,AngularJS 的依賴注入,以及 CommonJS,具體能夠看筆者的上一篇文章《JS模塊化歷史簡介》。當咱們學習 ES6 的模塊化,就會發現它的發展深受 CommonJS 的影響。經過這篇文章,咱們將看到 exportimport 語句,以及 ES6 模塊是怎麼與 CommonJS 模塊兼容的。html

嚴格模式

在 ES6 模塊系統中,嚴格模式是默認開啓的。嚴格模式是語言從語法層面限制你使用一些很差的寫法,因此它更嚴格(==)。它也讓編譯器更好地處理代碼。下面是 MDN上關於嚴格模式的解釋:嚴格模式es6

  • 變量必須顯式聲明
  • 函數的形參必須有惟一的名稱(不然會報語法錯誤)
  • 不能使用with
  • 給只讀的屬性賦值會報錯
  • 00840 這樣的八進制數字會報語法錯誤
  • 試圖 delete 沒法刪除的屬性會報錯
  • delete prop 會報語法錯誤,可使用 delete global[prop]
  • eval 不會在所在的詞法做用域引入新的變量
  • evalarguments 不能被改變或賦值
  • arguments 不會跟蹤方法的參數變化
  • arguments.callee 再也不支持,會報 TypeError
  • arguments.caller 再也不支持,會報 TypeError
  • 傳入方法內部的 this 再也不被強制轉換成 Object
  • fn.callerfn.arguments 再也不支持
  • 保留關鍵字 protectedstaticinterface 不能被綁定

即便在 ES6 中嚴格默認是默認開啓的,也推薦在每一個模塊中都使用 use strict 關鍵字。api

讓咱們先來看下 export 關鍵字吧~瀏覽器

export

在 CommonJS 中,導出模塊能夠用 module.exports 。從下面的代碼能夠看出,你能夠導出任何值:模塊化

module.exports = 1
module.export = NaN
module.exports = 'foo' 
module.exports = { foo: 'bar' } 
module.exports = ['foo', 'bar'] 
module.exports = function foo () {}

像 CommonJS 模塊同樣,ES6 模塊也是暴露 API 的文件。一樣的,ES6 模塊內部的聲明只在模塊內部有效。這就意味着,某個模塊中的變量,若是沒有被導出,在其餘模塊中就沒法使用。函數

Exporting a Default Binding

上面的 CommonJS 代碼若是用 ES6 語法寫起來也很類似,主要就是將 module.exports 替換爲 export default工具

export default 1 
export default NaN 
export default 'foo' 
export default { foo: 'bar' } 
export default ['foo', 'bar'] 
export default function foo () {}

而與 CommonJS 不一樣的是,export 語句只能放在 ES6 模塊代碼的頂層,就算放在一個當即執行的函數中也不行。可想而知,這種限制讓編譯器更容易解析 ES6 模塊,同時也讓避免了在方法中動態導出這種不是很實用的騷操做。學習

function foo () { 
    export default 'bar' // SyntaxError 
} 
foo()

導出語法不是隻有 export default,你也可使用具名導出。ui

Named Exports

在 CommonJS 中導出時也不是必須將 module.exports 賦值爲一個對象,你能夠直接改變它的屬性。this

module.exports.foo = 'bar'
module.exports.baz = 'ponyfoo'

好了,下面用 ES6 的寫法也很簡單(原文說了一堆不過重要就沒翻~):

export var foo = 'bar'
export var baz = 'ponyfoo'

有一句話要始終牢記,咱們導出的是綁定而不是值。

Bindings, Not Values

在 ES6 模塊中重要的一個點是:導出的是綁定,而不是值或者引用。這就意味着你導出的變量foo 被綁定在了模塊上,它的值改變了,外部也能收到變化。儘管一般狀況下不推薦在模塊加載後改變導出的值。

若是你有一個 ./a 模塊,導出的 foo 將在 500ms 後從 bar 變爲 baz

export var foo = 'bar'
setTimeout(() => foo = 'baz', 500)

除了默認綁定和具名綁定,咱們還能夠導出一個列表的綁定。

Exporting Lists

下面的代碼能夠看到,ES6 模塊容許咱們導出一個包含頂級成員的列表:

var foo = 'ponyfoo'
var bar = 'baz'
export { foo, bar }

若是你想改變其中變量的名字,能夠用如下語法:

export { foo as ponyfoo }

同時也能夠指定其中的某個爲默認導出:

export { foo as default, bar }

而一般的最佳實踐是隻使用 export default,而且將其放在模塊的底部。

Best Practices and export

若是同時使用命名導出,導出列表和默認導出,很容易形成困擾,因此大部分狀況下做者建議只使用 export default,而且將語句放在模塊文件的底部。你能夠將要導出的對象叫做 api

var api = {
    foo: 'bar',
    baz: 'ponyfoo'
}
export default api

這樣作有幾個好處:(原文比較囉嗦,下面提煉兩點)

  1. 將導出的內容包裹在一個對象中,在模塊內部能夠很容易找到導出的內容。
  2. 只使用 export default 具備一致性,不會由於過多導出方式形成混淆,在使用的時候也更加方便。

咱們已經熟悉了 export 的 API 和注意事項,下面來看 import 語句。

import

做爲與 export 相對的語句,import 可讓咱們導入另外一個模塊中的內容。模塊的加載方式,在瀏覽器端主要依靠 Babel 實現。而在內部 import 語句的實現也幾乎是和 CommonJS 的 require 語句同樣。

讓咱們用 lodash 來講明。下面的語句簡單地加載了 Lodash 模塊到咱們本身的模塊,它沒有建立任何變量,但它將會執行 lodash 模塊頂層代碼的內容。

import 'lodash'

在講導入綁定以前,咱們須要先明確的是,跟 export 語句相似,import 語句也只能在模塊的頂層代碼使用。這能讓編譯器更好地處理解析工做,也能幫助其餘靜態分析工具解析咱們的代碼。

Importing Default Exports

在 CommonJS 中,咱們使用 require 導入內容:

var _ = require('lodash')

在 ES6 模塊中,咱們只須要爲導入的綁定起一個名字:

import _ from 'lodash'

咱們也能夠導入具名導出的內容。

Importing Named Exports

這個語法跟 ES6 的解構賦值很類似,可是也不太同樣:

import { map, reduce } from 'lodash'

跟解構賦值不一樣的其中一點是,你能夠爲導入的綁定建立別名,能夠同時使用有別名和沒有別名的導入。

import { cloneDeep as clone, map } from 'lodash'

也能夠同時使用具名和默認的導入,若是要在花括號內使用默認導入,須要使用 default 關鍵字,固然也能夠給它起個別名,或者像第三行那樣:

import { default, map } from 'lodash'
import { default as _, map } from 'lodash'
import _, { map } from 'lodash'

import All The Things

咱們也能夠導入一個模塊中的整個命名空間。import * 這個語法後面必須跟一個別名,這個別名中就存放了導入模塊的全部綁定。若是裏面包含一個默認導出,那麼它放被放在了 alias.default

import * as _ from 'lodash'

Conclusions

咱們今天能夠直接使用 ES6 模塊,得益於 Babel 編譯器藉助了 CommonJS 模塊的實現。這其中的一個好處就是 CommonJS 和 ES6 模塊之間是兼容的,即咱們能夠在 ES6 模塊中直接寫 CommonJS 的語法。

ES6 模塊系統看起來很棒,而它也是 JavaScript 中的一個最重要的功能。但願在不久的未來,模塊加載 API 能夠最終敲定並直接在瀏覽器端實現。


原文連接

歡迎關注個人公衆號:碼力全開(codingonfire)
codingonfire.jpg

相關文章
相關標籤/搜索