ECMAScript的2015年(原名爲ES6)引入了一個全新的功能和概念,前端的JavaScript的世界,奇怪的是已經存在了很長一段時間-模塊。ES2015正式肯定什麼CommonJS的(對Node.js的模塊的基礎)和AMD曾試圖在試圖把全部的優點和漏下的弱點來解決:前端
- 緊湊語法
- 異步和可配置模塊加載
本文將重點ES2015模塊的語法和它的一些陷阱的。模塊的加載和包裝將在其餘時間覆蓋。java
爲何模塊?
若是這聽起來像一個明顯的問題,你也許能夠跳轉到文章中的下一節。若是你還在讀這篇文章,這裏是你的,爲何JavaScript須要的模塊基本底漆。webpack
目前最經常使用的JavaScript平臺是一個Web瀏覽器而設計的全部代碼的執行在一個單一的全球背景。這使得它很是具備挑戰性的編寫哪怕是很小的應用程序,而沒必要處理命名衝突。從本質上說,這一切都歸結爲代碼組織,而不是沒必要擔憂每次須要聲明一個新的一次重挫現有的變量。git
傳統的JavaScript應用程序是在文件的分區,並在製做的時候鏈接在一塊兒。當這個已經證實本身是至關繁瑣的,咱們就開始包裝每個文件中的IIFE:(function() { ... })();
。這種類型的構造建立一個本地範圍等模塊的想法構思。這之後表如今CommonJS的和AMD系統加載代碼,並推出的「模塊」爲JavaScript的概念。es6
換句話說,目前的「模塊」系統(AB)利用現有的語言構造賦予它新的功能。ES2015經過適當的語言功能正式肯定這些概念,並讓他們的官員。github
建立模塊
一個JavaScript模塊是出口一些其餘模塊消耗文件。請注意,咱們僅在瀏覽器中談到ES6 / 2015的模塊,不會被談論的Node.js如何組織的模塊系統。有幾件事情建立ES2015模塊時,要牢記:web
模塊有本身的適用範圍
不一樣於傳統的JavaScript,使用模塊時,你沒必要擔憂污染全局範圍。事實上,這個問題是徹底相反 - 你必須輸入你須要使用到每個模塊的一切。後來,可是,是一個更好的主意,由於你能夠清楚地看到全部的每一個模塊中使用的依賴。api
命名模塊
一個模塊的名字來自任何文件或文件夾名稱,則能夠省略.js
在兩種狀況下擴展。這裏是如何工做的:瀏覽器
- 若是你有一個指定的文件
utils.js
,你能夠經過導入./utils
相對路徑。 - 若是你有一個命名的文件
./utils/index.js
,您經過引用它./utils/index
或乾脆./utils
。這可讓你有一大堆的文件夾中,並將其導入爲單個模塊。
出口和進口
大多數模塊輸出一些功能的其餘模塊使用新的ES2015導入export
和import
關鍵字。模塊能夠導出和導入任何類型的一個或多個變量,是一Function
,Object
,String
,Number
,Boolean
等。
默認出口
每一個模塊均可以有一個,且只有一個,能夠導出和導入不指定變量名稱默認的導出。例如:
1
2
3
4
5
6
7
8
9
|
// hello-world.js
export default function() {}
// main.js
import helloWorld from './hello-world';
import anotherFunction from './hello-world';
helloWorld();
console.log(helloWorld === anotherFunction);
|
在等效CommonJS的將是:
1
2
3
4
5
6
7
8
9
|
// hello.js
module.exports = function() {}
// main.js
var helloWorld = require('./hello-world');
var anotherFunction = require('./hello-world');
helloWorld();
console.log(helloWorld === anotherFunction);
|
任何JavaScript值能夠從一個模塊做爲默認的導出:
1
2
3
|
export default 3.14;
export default {foo: 'bar'};
export default 'hello world';
|
命名出口
除了默認的出口住命名的出口。在這種狀況下,你必須明確地指定要導出並使用相同的名稱將其導入的變量名。一個模塊能夠有任意數量的任何類型的命名出口。
1
2
3
4
|
const PI = 3.14;
const value = 42;
export function helloWorld() {}
export {PI, value};
|
在等效CommonJS的將是:
1
2
3
4
5
|
var PI = 3.14;
var value = 42;
module.exports.helloWorld = function() {}
module.exports.PI = PI;
module.exports.value = value;
|
您還能夠更改出口名稱不重命名原來的變量,例如:
1
2
|
const value = 42;
export {value as THE_ANSWER};
|
在等效CommonJS的將是:
1
2
|
var value = 42;
module.exports.THE_ANSWER = value;
|
若是你有名稱衝突或只是想以不一樣的名稱比原來進口的變量,你可使用as
像這樣的關鍵字:
1
|
import {value as THE_ANSWER} from './module';
|
在等效CommonJS的將是:
1
|
var THE_ANSWER = require('./module'').value;
|
導入全部的事情
一個簡單的方法來導入全部的值從一個命令一個模塊是使用*
符號。該組中的全部模塊的匹配出口名稱的屬性的單一對象變量下的出口。默認出口放在下default
屬性。
1
2
3
4
5
6
7
8
9
10
|
// module.js
export default 3.14;
export const table = {foo: 'bar'};
export function hello() {};
// main.js
import * as module from './module';
console.log(module.default);
console.log(module.table);
console.log(module.hello());
|
在等效CommonJS的將是:
1
2
3
4
5
6
7
8
9
10
|
// module.js
module.exports.default = 3.14;
module.exports.table = {foo: 'bar'};
module.exports.hello = function () {};
// main.js
var module = require('./module');
console.log(module.default);
console.log(module.table);
console.log(module.hello());
|
值得一提的如何使用時,默認的出口進口的區別import * as foo from
和import foo from
。後來只進口default
出口和* as foo
進口的一切模塊出口做爲一個單一的對象。
導出全部的事情
至關廣泛的作法是在一個模塊中從另外一個轉口一些特定的值(甚至所有)。這就是所謂的再出口。被觸發請注意,您能夠在同一個「名」多個不一樣的值倍再出口沒有一個錯誤。在這種狀況下,去年出口值獲勝。
1
2
3
4
5
6
7
8
9
10
|
// module.js
const PI = 3.14;
const value = 42;
export const table = {foo: 'bar'};
export function hello() {};
// main.js
export * from './module';
export {hello} from './module';
export {hello as foo} from './module';
|
在等效CommonJS的將是:
1
2
3
4
5
6
7
8
|
// module.js
module.exports.table = {foo: 'bar'};
module.exports.hello = function () {};
// main.js
module.exports = require('./module');
module.exports.hello = require('./module').hello;
module.exports.foo = require('./module').hello;
|
陷阱
要明白,什麼是被導入到模塊都不引用或值,但綁定是很重要的。你能夠認爲它是「干將」,以居住模塊的身體裏面的變量。這致使一些行爲彷佛出人意料。
缺乏錯誤
當名字從一個模塊中導入變量,若是你犯了一個錯字或變量被刪除之後,沒有錯誤將在進口過程當中引起的,而不是進口的結合會undefined
。
1
2
3
4
5
6
|
// module.js
export const value = 42;
// main.js
import {valu} from './module'; // no errors
console.log(valu); // undefined
|
可變綁定
進口綁定是指一個模塊的身體裏面的變量。這將致使當您導入「按值」變量,如一個有趣的反作用Number
,Boolean
或String
。這多是由於該變量的值將經過操做所述的進口模塊以外被改變。換言之,一個「由值」變量可別處突變。下面是一個例子。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// module.js
export let count = 0;
export function inc() {
count++;
}
// main.js
import {count, inc} from './module; // `count` is a `Number` variable
assert.equal(count, 0);
inc();
assert.equal(count, 1);
|
另外,在上述的例子中count
變量是的Number
類型,但其值出如今改變main
模塊。
導入的變量是隻讀
不管是從一個模塊被導出什麼樣的聲明,進口變量老是隻讀的。你能夠,可是,改變進口對象的屬性。
1
2
3
4
5
6
7
8
9
|
// module.js
export let count = 0;
export const table = {foo: 'bar'};
// main.js
import {count, table} from './module;
table.foo = 'Bar'; // OK
count++; // read-only error
|
測試模塊
測試,或者更具體地說:存根和嘲笑由模塊輸出變量,遺憾的是並無被新的ES2015的模塊系統解決。就像使用CommonJS的,導出的變量不能從新分配。應對方法之一就是導出的對象,而不是單獨的變量。
1
2
3
4
5
6
7
8
9
10
|
// module.js
export default {
value: 42,
print: () => console.log(this.value)
}
// module-test.js
import m from './module';
m.value = 10;
m.print(); // 10
|
底線
ES2015模塊標準化的方式模塊加載和分辨率應該在現代的JavaScript來實現。之間是否使用的參數CommonJS的或AMD終於獲得瞭解決。
咱們獲得了緊湊型模塊的語法和靜態模塊定義能夠協助將來的編譯器優化,甚至類型檢查。如今,已經沒有必要對UMD在公開發行庫的樣板。
ES6今天
你怎麼能利用今天ES6特色?在過去幾年中使用transpilers已成爲常態。人與大公司再也不羞澀了。巴貝爾是一個ES6到ES5 transpiler,支持全部的ES6功能。
若是你正在使用相似Browserify或WebPACK中在JavaScript創建管道,增長巴貝爾transpilation 只須要一兩分鐘。還有就是,固然,對於幾乎每個共同的Node.js構建系統同樣咕嘟咕嘟,步兵和其餘許多人的支持。
怎麼樣的瀏覽器?
大多數瀏覽器都遇上上實現新的功能,但沒有一我的的全力支持。這是否意味着你在等什麼?這取決於。這是開始使用的語言特徵,將在1 - 2年內廣泛可用,以便您熟悉他們到時候是個好主意。在另外一方面,若是你以爲以上的源代碼100%控制的須要,你應該堅持ES5如今。