export default function, export default constどっち?
はじめに
Next.jsで開発していて、appフォルダ内のファイルはexport default function
とexport default const
どちらで記載した方が良いのか?と思い、それぞれの違いをまとめてみました。
ホイスティングについて
両者の違いは、ホイスティングされるかどうかです。
ホイスティング(持ち上げる(hoisted))とは、関数宣言や変数宣言がそのスコープの先頭に移動したように振る舞うというJavaScriptの仕組みです。
関数宣言
関数宣言は、関数が宣言された場所に関わらず使えるようになるのは、関数宣言がスコープの先頭に「持ち上げられる」からです。
console.log(greet()); // "Hello!"
function greet() {
return "Hello!";
}
上記のコードでは、関数宣言function greet()
はコードが実行される前に「持ち上げられて」関数として使える状態になるため、greet
関数が呼ばれる前に宣言されています。
内部的には、JavaScriptエンジンは関数宣言を以下のように扱います:
function greet() {
return "Hello!";
}
console.log(greet()); // "Hello!"
変数宣言
変数宣言でも、変数は持ち上げられますが、初期化(値の代入)は持ち上げられません。
そのため下記の例では、console.log(myVar)
はundefined
を出力しますが、10にはなりません。変数宣言はホイスティングされても、初期化は実行時に行われるためです。
console.log(myVar); // undefined
var myVar = 10;
アロー関数
アロー関数には、ホイスティングが適用されません。
そのため、関数を定義する前に呼び出すとエラーになります。
下記の例では、const
で定義されたgreet
変数は、実行時に初期化されるまで関数として利用することができません。そのため、console.log(greet())
を関数定義の前で呼び出すと、エラーが発生します。
console.log(greet()); // エラー:greet is not a function
const greet = () => {
return "Hello!";
};
export default functionとexport default constの違い
上記で述べた違いから、export default functionとexport default constを比べてみます。
export default function
export default function
の関数宣言は、ホイスティングされるため、関数がスコープの先頭に持ち上げられるために予期しない動作が発生することがあります。
また、この書き方は一見便利に見えるものの、関数宣言の位置に依存しているため、コードが長くなったり複雑になったりする場合、可読性が下がる可能性があります。
// 普通の関数宣言を使う例
export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>Welcome to the About page!</p>
</div>
);
}
export default const
アロー関数を使う場合はホイスティングが発生しないため、コードの順番を気にせず書けるという利点があります。
また、こちらの書き方の方がモダンなJavaScript/TypeScriptの書き方として広く使われており、
型推論(特にTypeScriptの場合)を活用しやすいです。
// アロー関数を使う例
const AboutPage: React.FC = () => {
return (
<div>
<h1>About Us</h1>
<p>Welcome to the About page!</p>
</div>
);
};
export default AboutPage;
結論
微妙な違いしかありませんが、個人的には以下の理由からアロー関数を使うのが良いと思います。
- よりモダンなJavaScript/TypeScriptの書き方として広く使われている。
- 型推論しやすい
- コンポーネントの再代入を防ぐことができ、予期しないバグを避けられる。
Discussion
export default let で宣言すれば いわゆる参照渡しで 関数をすげ替えできるので便利ですね。
(代入が import 元にも反映されるのでとても便利。もちろん import 元では 読み取り専用なので上書きされる心配もない。 ただ、代入すると 変数との参照は切れる(いわゆる値渡し)なのですげ替えはされない。