概要
Pythonista だからョ……。
いやあ前回の CommonJS も “Python でたとえると” シリーズにしたかったんだよ。
だけど不可能だった。その理由が今回わかったよ。 CommonJS は JavaScript の言語仕様ではなく、 “JavaScript をうまく使う実行環境の仕様” だったからだ (そんなものは Python にはない)。対して ES Modules は ECMAScript (JavaScript の標準規格) で定められた言語仕様だから、 “Python でたとえると” がやりやすかったよ。
ES Modules の解説を Python の話をしながらする
- ES Modules のモジュールシステムでは、エクスポートしたい変数を
export
keyword を使って外部へ公開する。- Python でたとえろって? 普通の変数定義のことだ。 Python では “デフォで全部 public 変数” “private にしたかったらひと工夫必要” だけど ES Modules では逆だってコト。
- ただしいっこ特別な変数
default
がある。これは予約語で、エクスポート専用の変数。まあ気にしなくていい。やりたいことはdefault
なしで全部できる。 - インポートするときは、
import defaultItem, { VAR1, VAR2 } from MODULE
かimport * as module from MODULE
のどっちかでやる。- Python でたとえろって?
from MODULE import VAR1, VAR2
かimport MODULE
を使えってこと。
- Python でたとえろって?
“ES Modules の実行環境では、 export
keyword を使ってエクスポートして、 Python の from ... import ...
か import ...
にあたるようなモノでインポートする” 以上だ。
ES Modules と CommonJS がややこしい問題を緑さんがどう切り抜けたか
“ES Modules のほうが言語仕様だから keyword を使ってるほうが ES Modules” これがすべてだ。 keyword は予約語とも言われるヤツで、 Python では import keyword; print(keyword.kwlist)
とかで一覧を見ることができるぜ。
// CommonJS がエクスポートと呼んでいるものは、
// 実際のところ module.exports オブジェクトへの代入、または値追加に過ぎない。
// インポートも関数のパワーを使っている。
exports.increment = (i) => i + 1;
module.exports.sunday = "Sunday";
console.info(require("./foo.js"))
// ES Modules は言語仕様だから keyword をガンガン使う。
const a = "A"
export { a }
import { a } from "./foo.js"
以下、実験ノート
node -v
# v20.3.0
# とりあえずエクスポート側を作ろうぜ。
echo '
export default "DEFAULT"
const a = "A"
export { a }
export const b = "B"
' > export-side.mjs
# Python でいうところの from ... import ... を試そう。
echo '
import defaultItem, { a, b } from "./export-side.mjs"
console.info({ defaultItem, a, b })
' > import-side.mjs
node import-side.mjs
# { defaultItem: 'DEFAULT', a: 'A', b: 'B' }
# で、ひとつめ (default のほう) とふたつめ (a, b のほう) は
# どっちを省略してもいい。
echo '
import defaultItem from "./export-side.mjs"
console.info({ defaultItem })
' > import-side.mjs
node import-side.mjs
# { defaultItem: 'DEFAULT' }
echo '
import { a, b } from "./export-side.mjs"
console.info({ a, b })
' > import-side.mjs
node import-side.mjs
# { a: 'A', b: 'B' }
# Python でいうところの import ... を試そう。
echo '
import * as imported from "./export-side.mjs"
console.info({ imported })
' > import-side.mjs
node import-side.mjs
# {
# imported: [Module: null prototype] { a: 'A', b: 'B', default: 'DEFAULT' }
# }
# とりあえず気になるのは const default はできるの? ってコトだよな。不可だ。
# Python でも if とかは変数名として使用不可。
echo '
const default = "DEFAULT"
' > export-side.mjs
node export-side.mjs
# const default = "DEFAULT"
# ^^^^^^^
# SyntaxError: Unexpected token 'default'
# あと気になるのは、 `imported: [Module: null prototype]` これなに? ってコトだよな。
# ぼくは Python の親クラスみたいなものかなと思ったのだけど、違うらしい。
# これは JavaScript のプロトタイプベースの継承とかいうみたい。
# 今回は作り方だけ押さえておく。
echo '
const normal = {}
normal.default = "DEFAULT"
normal.a = "A"
normal.b = "B"
const obj = Object.create(null)
obj.default = "DEFAULT"
obj.a = "A"
obj.b = "B"
console.info({ normal, obj })
' > prototype-test.mjs
node prototype-test.mjs
# {
# normal: { default: 'DEFAULT', a: 'A', b: 'B' },
# obj: [Object: null prototype] { default: 'DEFAULT', a: 'A', b: 'B' }
# }
# 作れた!