[譯]JavaScript ES6模塊指南

前言

ECMAScript 2015(又稱ES6)提供了一個前端JavaScript缺失已久的特性 —— 模塊。ES2015中的模塊參考了CommonJS規範(目前Node.js的模塊規範)以及AMD規範,而且儘量的取其精華,去其糟粕:javascript

  • 它提供了簡潔的語法前端

  • 以及異步的,可配置的模塊加載java

這篇文章將會專一於ES2015的模塊語法以及注意點。關於模塊的加載和打包,將會在另外一篇文章中細述。es6

爲何要使用模塊?

目前最廣泛的JavaScript運行平臺即是瀏覽器,在瀏覽器中,全部的代碼都運行在同一個全局上下文中。這使得你即便更改應用中的很小一部分,你也要擔憂可能會產生的命名衝突。瀏覽器

傳統的JavaScript應用被分離在多個文件中,而且在構建的時候鏈接在一塊兒,這稍顯笨重。因此人們開始將每一個文件內的代碼都包在一個自執行函數中:(function() { ... })();。這種方法建立了一個本地做用域,因而最初的模塊化的概念產生了。以後的CommonJS和AMD系統中所稱的模塊,也是由此實現的。異步

換句話說,現存的「模塊」系統是使用已有的語言特性所實現的。而ES2015則經過添加適當的新的語言特性,來使之官方化了。模塊化

建立模塊

一個JavaScript模塊就是一個對其餘模塊暴露一些內部屬性/方法的文件。咱們在這裏僅會討論瀏覽器中的ES2015模塊系統,並不會涉及Node.js是如何組織它自身的模塊的。一些在建立ES2015模塊時須要注意的點:函數

每一個模塊都有本身的上下文

和傳統的JavaScript不一樣,在使用模塊時,你沒必要擔憂污染全局做用域。偏偏相反,你須要把因此你須要用到的東西從其餘模塊中導入進來。可是,這樣也會使模塊之間的依賴關係更爲清晰。oop

模塊的名字

模塊的名字由它的文件名或文件夾名所決定,而且你能夠忽略它的.js後綴:測試

  • 若是你有一個叫utils.js的文件,那麼你能夠經過./utils這樣的相對路徑導入它

  • 若是你有一個叫./utils/index.js的文件,則你能夠經過./utils/index./utils來導入它。這使得你能夠批量導入一個文件夾內的全部模塊。

導出和導入

可使用ES2015的新關鍵字importexports來導入或導出模塊中的東西。模塊能夠導入和導出各類類型的變量,如函數,對象,字符串,數字,布爾值,等等。

默認導出

每個模塊都支持導出一個不具名的變量,這稱做默認導出:

// hello-world.js
export default function() {}
 
// main.js
import helloWorld from './hello-world';
import anotherFunction from './hello-world';
 
helloWorld();
console.log(helloWorld === anotherFunction);

等價的CommonJS語法:

// hello.js
module.exports = function() {}
 
// main.js
var helloWorld = require('./hello-world');
var anotherFunction = require('./hello-world');
 
helloWorld();
console.log(helloWorld === anotherFunction);

任何的JavaScript值均可以被默認導出:

export default 3.14;
export default {foo: 'bar'};
export default 'hello world';

具名導出

除了默認導出外,ES2015的模塊系統還支持導出任意數量個具名的變量:

const PI = 3.14;
const value = 42;
export function helloWorld() {}
export {PI, value};

等價的CommonJS語法:

var PI = 3.14;
var value = 42;
module.exports.helloWorld = function() {}
module.exports.PI = PI;
module.exports.value = value;

你也能夠在導出變量時對其重命名:

const value = 42;
export {value as THE_ANSWER};

等價的CommonJS語法:

var value = 42;
module.exports.THE_ANSWER = value;

在導入時,你也可使用as關鍵字來重命名導入的變量:

import {value as THE_ANSWER} from './module';

等價的CommonJS語法:

var THE_ANSWER = require('./module'').value;

導入全部

最簡單的,在一條命令中導入一個模塊中全部變量的方法,是使用*標記。這樣一來,被導入模塊中全部導出的變量都會變成它的屬性,默認導出的變量則會被置於default屬性中。

// 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語法:

// 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 fromimport foo from的區別。後者僅僅會導入默認導出的變量,而前者則會在一個對象中導入全部。

導出全部

一個可能的需求是,你須要將另外一個模塊中的一些(或全部)值在你的模塊中再次導出,這被稱做二次導出(re-exporting)。值得注意的是,你能夠二次導出許多同名的值,這將不會致使異常,而是最後一個被導出的值將會得到勝利。

// 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語法:

// 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;

注意點

一個關鍵點時,導入模塊的東西,並非一個引用或一個值,而是一個相似與被導入模塊內部的一個getter對象。因此這可能會致使一些不符合預期的行爲。

缺少異常

在具名地導入其餘模塊的變量時,若是你不當心打錯了變量名,這將不會拋出異常,而是導入的值將會變成undefined

// module.js
export const value = 42;
 
// main.js
import {valu} from './module'; // no errors
console.log(valu); // undefined

可變的基本類型值

在導入一些基本類型的值(如數字,布爾值或字符串)時,可能會產生一個有趣的反作用。這些值可能會在模塊外被修改。例子:

// 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變量是一個數值類型,它在main模塊中被修改了值。

導入的變量是隻讀的

不論你以何種聲明導出變量,它們都是隻讀的。可是,若是導出的是對象,你能夠改變變量的屬性。

// 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

測試模塊

若是想要測試,或mock被導出的變量,很不幸,這在新的ES2015模塊系統中是辦不到的。由於與CommonJS同樣,導出的變量在外面不能被從新賦值。惟一的解決辦法是,導出一個單獨的對象。

// 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的模塊標準化了模塊的加載和解析方式。CommonJS和AMD之間的爭論終於被解決了。

咱們獲得了更簡潔的模塊語法,以及靜態的模塊定義,這有助於編譯器的優化,甚至是類型檢查。

原文連接:https://strongloop.com/strongblog/an-introduction-to-javascript-es6-modules/

相關文章
相關標籤/搜索