概要
前回、 CommonJS のエクスポートについての緑さん研究ノートを書いた。
この研究ノートでは、 module
というモジュールスコープ変数が、モジュール内の値をどう外部へエクスポートするか、というところに主眼を置いていた。でもここまで来たら、インポートを行う側である require
の中身もちょっと覗いてみたくなるものだ。
といって覗いてみたら面白いコトになっていて最終的に Python の話になっちゃったハナシするぞ。コイツいつも Python の話してんな。
require
実験ノート
とりあえず覗いてみようぜ。
node -v
# v20.3.0
node --eval 'console.info({ require })'
# {
# require: [Function: require] {
# resolve: [Function: resolve] { paths: [Function: paths] },
# main: undefined,
# extensions: [Object: null prototype] {
# '.js': [Function (anonymous)],
# '.json': [Function (anonymous)],
# '.node': [Function (anonymous)]
# },
# cache: [Object: null prototype] {}
# }
# }
うん? なんか正直、 require
は function ですよ、と言われるくらいのものだと思っていたのだけど、色々出てきたな。どうやら require
は関数であると同時にオブジェクトでもあるらしい。
require.main
に注目してみたら結局 Python の話になった
まず注目してみたいのは require.main
かな。コマンドライン上で見てみると undefined
だけれど、ファイルの中で require
を見ると、そのファイルの module
が入っているんだよ。
echo 'console.info({ require })' > baz.js
node baz.js
# {
# require: [Function: require] {
# resolve: [Function: resolve] { paths: [Function: paths] },
# main: Module {
# id: '.',
# path: '/Users/user/Documents/GitHub/ts-lab/js-commonjs-export-lab',
# exports: {},
# filename: '/Users/user/Documents/GitHub/ts-lab/js-commonjs-export-lab/baz.js',
# loaded: false,
# children: [],
# paths: [Array]
# },
# extensions: [Object: null prototype] {
# '.js': [Function (anonymous)],
# '.json': [Function (anonymous)],
# '.node': [Function (anonymous)]
# },
# cache: [Object: null prototype] {
# '/Users/user/Documents/GitHub/ts-lab/js-commonjs-export-lab/baz.js': [Module]
# }
# }
# }
調べてみると、実は “ファイルの module
が入っている” わけではなくて、 node で起動した最初のモジュールの module
が入っているらしい。つまり、 Python の if __name__ == "__main__"
が JavaScript でも出来るってことだよ! な、なんだってーー!!
echo 'if (require.main === module) { console.info("I am main!"); }' > qux.js
# qux.js を起動すると、この if 文が true になるが……
node qux.js
# I am main!
# qux.js を別ファイルから呼び出すと、この if 文は false になる。
node --eval 'require("./qux.js")'
# (なんも表示されない)
関数であると同時にオブジェクトでもあるってところに注目してみたら結局 Python の話になった
え、 JavaScript って関数と同時にオブジェクト、なんてコトができるの?
node --eval 'const foo = (x, y) => x + y;
foo.bar = 1;
console.info(foo.bar);
console.info(foo(1, 2));
console.info({ foo });'
# 1
# 3
# { foo: [Function: foo] { bar: 1 } }
できたよ。マジか。やれやれ、こんな危なっかしいコト、ぼくの親愛なる Python には出来な……
def foo(x: int, y: int) -> int:
return x + y
foo.bar = 1
print(foo.bar) # 1
print(foo(1, 2)) # 3
print(type(foo), vars(foo)) # <class 'function'> {'bar': 1}
出来んのかい!!
まあもちろん、型ヒント的にはめちゃくちゃなコトやっているから、 mypy
でチェックすると怒られるけどな。このように↓
foo.bar = 1
# "Callable[[int, int], int]" has no attribute "bar" [attr-defined]
ちゃんと型ヒントを書くとしたら、こうしないといけないね↓
class Foo:
def __init__(self):
self.bar = 0
def __call__(self, x: int, y: int) -> int:
return x + y
foo = Foo()
foo.bar = 1
print(foo.bar) # 1
print(foo(1, 2)) # 3
print(type(foo), vars(foo)) # <class '__main__.Foo'> {'bar': 1}
うーん、良い Python だ。 Callable であり、 bar
というインスタンス変数を持つことが明確で、良いね。 JavaScript (CommonJS) の調査をしていたはずが、どうしても思考が Python にイッちまうな。