在 ES6 以前,咱們已經知道了 RequireJS,AngularJS 的依賴注入,以及 CommonJS,具體能夠看筆者的上一篇文章《JS模塊化歷史簡介》。當咱們學習 ES6 的模塊化,就會發現它的發展深受 CommonJS 的影響。經過這篇文章,咱們將看到 export
和 import
語句,以及 ES6 模塊是怎麼與 CommonJS 模塊兼容的。html
在 ES6 模塊系統中,嚴格模式是默認開啓的。嚴格模式是語言從語法層面限制你使用一些很差的寫法,因此它更嚴格(==)。它也讓編譯器更好地處理代碼。下面是 MDN上關於嚴格模式的解釋:嚴格模式es6
with
00840
這樣的八進制數字會報語法錯誤delete
沒法刪除的屬性會報錯delete prop
會報語法錯誤,可使用 delete global[prop]
eval
不會在所在的詞法做用域引入新的變量eval
和 arguments
不能被改變或賦值arguments
不會跟蹤方法的參數變化arguments.callee
再也不支持,會報 TypeError
arguments.caller
再也不支持,會報 TypeError
this
再也不被強制轉換成 Object
fn.caller
和fn.arguments
再也不支持protected
,static
,interface
不能被綁定即便在 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 模塊內部的聲明只在模塊內部有效。這就意味着,某個模塊中的變量,若是沒有被導出,在其餘模塊中就沒法使用。函數
上面的 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
在 CommonJS 中導出時也不是必須將 module.exports
賦值爲一個對象,你能夠直接改變它的屬性。this
module.exports.foo = 'bar' module.exports.baz = 'ponyfoo'
好了,下面用 ES6 的寫法也很簡單(原文說了一堆不過重要就沒翻~):
export var foo = 'bar' export var baz = 'ponyfoo'
有一句話要始終牢記,咱們導出的是綁定而不是值。
在 ES6 模塊中重要的一個點是:導出的是綁定,而不是值或者引用。這就意味着你導出的變量foo
被綁定在了模塊上,它的值改變了,外部也能收到變化。儘管一般狀況下不推薦在模塊加載後改變導出的值。
若是你有一個 ./a
模塊,導出的 foo
將在 500ms 後從 bar
變爲 baz
:
export var foo = 'bar' setTimeout(() => foo = 'baz', 500)
除了默認綁定和具名綁定,咱們還能夠導出一個列表的綁定。
下面的代碼能夠看到,ES6 模塊容許咱們導出一個包含頂級成員的列表:
var foo = 'ponyfoo' var bar = 'baz' export { foo, bar }
若是你想改變其中變量的名字,能夠用如下語法:
export { foo as ponyfoo }
同時也能夠指定其中的某個爲默認導出:
export { foo as default, bar }
而一般的最佳實踐是隻使用 export default
,而且將其放在模塊的底部。
export
若是同時使用命名導出,導出列表和默認導出,很容易形成困擾,因此大部分狀況下做者建議只使用 export default
,而且將語句放在模塊文件的底部。你能夠將要導出的對象叫做 api
:
var api = { foo: 'bar', baz: 'ponyfoo' } export default api
這樣作有幾個好處:(原文比較囉嗦,下面提煉兩點)
export default
具備一致性,不會由於過多導出方式形成混淆,在使用的時候也更加方便。咱們已經熟悉了 export
的 API 和注意事項,下面來看 import
語句。
import
做爲與 export
相對的語句,import
可讓咱們導入另外一個模塊中的內容。模塊的加載方式,在瀏覽器端主要依靠 Babel 實現。而在內部 import
語句的實現也幾乎是和 CommonJS 的 require
語句同樣。
讓咱們用 lodash
來講明。下面的語句簡單地加載了 Lodash 模塊到咱們本身的模塊,它沒有建立任何變量,但它將會執行 lodash
模塊頂層代碼的內容。
import 'lodash'
在講導入綁定以前,咱們須要先明確的是,跟 export
語句相似,import
語句也只能在模塊的頂層代碼使用。這能讓編譯器更好地處理解析工做,也能幫助其餘靜態分析工具解析咱們的代碼。
在 CommonJS 中,咱們使用 require
導入內容:
var _ = require('lodash')
在 ES6 模塊中,咱們只須要爲導入的綁定起一個名字:
import _ from 'lodash'
咱們也能夠導入具名導出的內容。
這個語法跟 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'
咱們今天能夠直接使用 ES6 模塊,得益於 Babel 編譯器藉助了 CommonJS 模塊的實現。這其中的一個好處就是 CommonJS 和 ES6 模塊之間是兼容的,即咱們能夠在 ES6 模塊中直接寫 CommonJS 的語法。
ES6 模塊系統看起來很棒,而它也是 JavaScript 中的一個最重要的功能。但願在不久的未來,模塊加載 API 能夠最終敲定並直接在瀏覽器端實現。
歡迎關注個人公衆號:碼力全開(codingonfire)