今回の問題
node.jsでサーバー関係のアプリを開発をしていて先達のgithubコードを見ていて node でヘッダ部分の定義にある require 文の使い方をよく理解していない自分に気づいた。
出くわした問題とそれについて少し調べ、自分なりの結論を得たので書き留めた。
モジュールを使うということ
node.jsで別ファイルに定義されている変数や関数を利用する。
モジュールを使ったコードの例(自分にしっくりくるもの)
mymath.js
module.exports.add = (a,b) => a+b
module.exports.subtract = (a,b) => a-b
使う側
index.js
const ddd = require('./mymath')
console.log(ddd.add(1,2))
console.log(ddd.subtract(1,2))
node index.js
とする。
同じパスにおいてあれば、require()で呼び出すときに引数に mymath.js を与えてあげるとその中で定義されている関数などを利用することができる、というもの。
出力結果
3
-1
module.exports = ?? の形
ここまでの表記ならばなんとなくしっくりと定義して使う、までできそうかなと思った。
ところが自分がやりたいことを調べているうちに以下のようなコードにぶつかった。
unknown.js
mudule.exports = storage;
詳細を追って説明していきます
そもそもは必要なモジュールが外部ファイルというよりディレクトリとして require されていた。
const gc = require("../config/");
というふうに読んである部分を見て「?」となった。
ディレクトリを require ???
ディレクトリをrequireで読む場合の挙動
const gc = require("../config");
としてある場合、そのディレクトリにindex.jsがあればそれを参照するらしい。
そのディレクトリにあった index.js を見に行ってみた
const storage = new Storage({
keyFilename: myfilename,
projectId: "myprojectrevolution"
});
module.exports = storage;
となっていた。ここで module.exports???? ってなった。
最初の方の例だと
module.exports.add =(a,b)=> a+b;
みたいな感じで exports する関数名(add)が明示的だったのに対して module.exports って言われると抽象的すぎで怖いなぁと直感的には感じた。
module.exports って何?
(前略)1つだけをエクスポートするモジュールがある場合などは module.exports を使用するのがより一般的です(後略)
ということらしい。
exports は module.exports のショートカットのようなもので、初期状態では exports は module.exports への参照です。
ともある(参考サイト1)。
なので、自分の理解としては最初に書いたコード群のように module.exports.object_name とか module.exports.function_name みたいなのが外部へのexportの方法として、一般的な書き方で exports するのがそのファイルからはひとつだけだよ、というときにこの記法を使うという理解をすることにした。
自分でも試してみた
以下のようなコードを書いて確かめてみた
mysingle.js
const single_obj = {
name: "kuntaro",
type: "human"
}
module.exports = single_obj;
単純にオブジェクトを定義して、それを module.exports としている。
index.js
const kunobj = require('./mysingle');
console.log(kunobj);
としてやると出力は
{ name: 'kuntaro', type: 'human' }
となる。
- 起きていることは、mysingle.js の中で定義したオブジェクト→外部でも利用できるようになった。
- 呼び出し側(index.js)で、requireで引用→ const で定義した名前(kunobj)で新たに定義できる
自分が混乱した理由は
- module.exports = hoge; という記述についてよくわからなかった
- requireで返ってきたオブジェクトを新しい変数名で定義できるので、モジュール側で定義した名前が無視される
という二点かと思います。皆様はそんなことなかったでしょうか?
ちなみに上の例を少し深堀して複数のオブジェクトの定義をした場合には
重複するかもしれませんが試してみました。
const single_obj = {
name: "kuntaro",
type: "human"}
const second_obj = {
name: "kentaro",
type: "dog"}
const third_obj = {
name: "kontaro",
type: "cat"}
module.exports.single = single_obj;
module.exports.second = second_obj;
module.exports.third = third_obj;
index.js
const jjj = require('./mymulti.js')
console.log(jjj);
の出力結果
{
single: { name: 'kuntaro', type: 'human' },
second: { name: 'kentaro', type: 'dog' },
third: { name: 'kontaro', type: 'cat' }
}
自分としてはこのように大本にモジュールで定義した変数名なりをそのまま使ってもらうほうが良いような気がする。
が、その場合には結局は使うときにモジュールで定義されている変数名や関数名を都度思い出さないといけなくなる。
小規模なアプリならいいけど、もっと大きなモジュールライブラリ(?)を利用するときのために、require を都度便利に使いこなすことが重要で、種々のスタイルで利用されるのに読むのも書くのも慣れていかないといけないなと感じました。
参考させて頂いたサイト
参考サイト1
https://www.webdesignleaves.com/pr/jquery/node-js-module-exports.html