JavaScriptには元来、モジュール機能が存在しなかった。
そこで、Node.jsではmodule.exports
という機能を導入してモジュール機能を実現し、さらに、その機能をブラウザでも利用できるようにするためのツールとしてbrowserify
などが開発された。
そういった話については以下を参照。
Node.jsのexportsについて
Node.jsの概要(1) モジュール機能
だがES2015でついに、JavaScriptの標準仕様としてモジュール機能が策定された。
Babelによるトランスパイルが必須
このモジュール機能は、仕様として存在しているだけで、現時点では実装はほとんど進んでいない。
ブラウザでもそうだし、Node.jsでも動かない(v6.4.0
で確認)。
そのため、実際に動かすためにはトランスパイルが必要になる。
以後、この記事では、バージョン6.14.0
のBabelでトランスパイルしてコードを実行していく。
(参考)Babelの導入
まずbabel-cli
をグローバルインストール。
npm install -g babel-cli
次に、作業用のルートディレクトリで、プリセットに関する作業を行う。
echo '{ "presets": ["es2015"] }' > .babelrc npm install babel-preset-es2015
これで、使えるようになる。
使い方は以下。
babel-node 実行するファイル名
基本的な使い方
基本的な考え方はNode.jsにおけるモジュール機能と同じで、export
したものをimport
すればいい。
以後、この記事では、export.js
のコードをimport.js
で読み込んで実行するという形を取る。
// export.js const myStr = '文字列'; function myFunc(){ return '関数'; }; export {myStr, myFunc}; export const myNum = 10; export function myFunc2(){ return '関数2'; }; // import.js import {myStr, myFunc, myNum, myFunc2} from './export.js'; console.log(myNum); // 10 console.log(myStr); // 文字列 console.log(myFunc()); // 関数 console.log(myFunc2()); // 関数2
この状態でbabel-node import.js
を実行すると、コードが動く。
変数や関数の宣言の前にexport
を付けるだけで、その変数や関数をモジュールとして使えるようになる。
また、{}
で囲むことで、既に存在する変数や関数をまとめてexport
することも出来る。
import
するには、以下の構文を使う。
import { 読み込む変数や関数の名前 } from '読み込むファイルのパス';
as
as
を使うことで、export
やimport
の際に名前を付けることも出来る。
exportでのas
// export.js const myStr = '文字列'; function myFunc(){ return '関数'; }; export {myStr as A, myFunc as B}; // import.js import {A,B} from './export.js'; console.log(A); // 文字列 console.log(B()); // 関数
importでのas
// export.js const myStr = '文字列'; function myFunc(){ return '関数'; }; export {myStr, myFunc}; // import.js import {myStr as X, myFunc as Y} from './export.js'; console.log(X); // 文字列 console.log(Y()); // 関数
全てのexportを一つのオブジェクトとしてまとめてimportする
import * as 任意の名前 from '読み込むファイルのパス';
この構文を使うと、任意の名前のオブジェクトが作られ、export
されているものが全て、そのオブジェクトのプロパティとして格納される。
// export.js const myStr = '文字列'; function myFunc(){ return '関数'; }; export {myStr, myFunc}; // import.js import * as importObj from './export.js'; console.log(importObj.myStr); // 文字列 console.log(importObj.myFunc()); // 関数 console.log(Object.getOwnPropertyNames(importObj)); // [ '__esModule', 'myStr', 'myFunc' ]
default
export
の後にdefault
をつけることも出来る。
そうすると、import
の際に任意の名前を付けることができ、{}
で囲む必要もない。
// export.js let myNum = 10; export default myNum; // import.js import data from './export.js' console.log(data); // 10
default
は、一つのファイルに一つしか指定できない。
default
とそれ以外を組み合わせることも可能。
// export.js let num = 10; let bool = true; export default function returnHello(){ return 'Hello'; }; export {num, bool}; // import.js import myFunc, {num, bool} from './export.js' console.log(myFunc()); // Hello console.log(num); // 10 console.log(bool); // true
*でのdefault
先述の*
を使った場合、default
でexport
されている対象は、default
という名前のプロパティとして格納される。
// export.js let num = 10; let bool = true; export default function returnHello(){ return 'Hello'; }; export {num, bool}; // import.js import * as importObj from './export.js' console.log(importObj.default()); // Hello console.log(importObj.num); // 10 console.log(importObj.bool); // true console.log(Object.getOwnPropertyNames(importObj)); // [ '__esModule', 'default', 'num', 'bool' ]
npmパッケージの読み込み
npmでインストールしたパッケージは、require
で読み込む時と同様、パッケージ名のみを指定すればよい。
// npm install react を行ってからこのファイルを実行する import React from 'react'; console.log(React.hasOwnProperty('createClass')); // true console.log(Object.getOwnPropertyNames(React)); // [ 'Children', // 'Component', // 'PureComponent', // 'createElement', // 'cloneElement', // 'isValidElement', // 'PropTypes', // 'createClass', // 'createFactory', // 'createMixin', // 'DOM', // 'version', // '__spread' ]
module.exportsとの共通点
以下の記事に書いたような、読み込まれたファイルは1度だけ実行される、クロージャのような仕組みを作れる、などの特徴は、ES2015にも共通している。
module.exportsとの相違点
module.exports
で読み込んだモジュールは自由に変更できるが、ES2015のモジュールは、読み込み専用となる。
変更を行おうとすると、エラーになる。
// export.js export let ESnum = 5; export let ESobj = { prop: 'オブジェクトのプロパティ' }; module.exports.num = 5; module.exports.obj = { prop: 'オブジェクトのプロパティ' }; // import.js import {ESnum, ESobj} from './export.js' let requireModule = require('./export.js'); console.log(ESnum); // 5 console.log(ESobj); // { prop: 'オブジェクトのプロパティ' } console.log(requireModule.num); // 5 console.log(requireModule.obj); // { prop: 'オブジェクトのプロパティ' } // requireで読み込んだものは、自由に操作できる requireModule.num++; console.log(requireModule.num); // 6 requireModule.obj.prop = '変更'; console.log(requireModule.obj); // { prop: '変更' } requireModule.obj = 'オブジェクトを文字列に変更'; console.log(requireModule.obj); // オブジェクトを文字列に変更 // ES2015でも、オブジェクトのプロパティは操作できる ESobj.prop = 'プロパティを変更'; console.log(ESobj); // { prop: 'プロパティを変更' } // 以下はエラーになる ESnum++; // "ESnum" is read-only ESobj = {}; // "ESobj" is read-only