Posted at

ES6 Modules 間では export/import された変数(?)は同期される

More than 3 years have passed since last update.

ES6 Modules に関する小ネタです。ちょっと言葉では説明しにくいのでコードを見てください。


foobar.js

export var foo = 'Foo';

export var bar = {
changeFoo() {
foo = 'Bar';
}
};


index.js

import { foo, bar } from './foobar';

console.log(foo);
bar.changeFoo();
console.log(foo);


babel-node index.js と実行すると Foo Bar と 出力されます。Foo Foo ではありません。Node.js で CommonJS を書いていた人からすると、???となる挙動ですね。index.jsでの foo はただの変数ではないのです。


CommonJS なら・・・

CommonJS で素直に同じようなモジュールを書こうとすると、以下のようになると思います。これだと当然 Foo Foo と出力されますね。bar.changeFoo() を呼んでも foobar.jsmodule.exports.foo も更新されませんし、index.jsfoo も更新されません。


foobar.js

var foo = 'Foo';

module.exports.foo = foo;
var bar = {
changeFoo: function () {
foo = 'Bar';
}
};
module.exports.bar = bar;


index.js

var foo = require('./foobar').foo;

var bar = require('./foobar').bar;

console.log(foo);
bar.changeFoo();
console.log(foo);



ES6 Modules の仕様(?)

どうやらこれは ES6 Modules の仕様のようです。仕様のどこに書いてあるかはわからないのですが、Babel でも Traceur でもこういう挙動をするので・・・。

Babel でコンパイルした結果は以下のようになっています。


foobar.js

'use strict';

Object.defineProperty(exports, '__esModule', {
value: true
});
var foo = 'Foo';
exports.foo = foo;
var bar = {
changeFoo: function changeFoo() {
exports.foo = foo = 'Bar';
}
};
exports.bar = bar;



index.js

'use strict';

var _foobar = require('./foobar');

console.log(_foobar.foo);
_foobar.bar.changeFoo();
console.log(_foobar.foo);


foo は変数ではなく Object の property として参照され、常に値が同期されるようになっていることがわかります。


だからなに?

だからどうだということもないのですが、(そういう設計が良いか悪いかは別として)変更される状態を持つ module を書く場合、以下のように無理に Object にぶら下げなくても

export default {

foo: 123,
bar: 234
};

気軽に直接 export できますね。

export var foo = 123;

export var bar = 234;

おわり。