はじめに
「constこそが唯一神である」というふざけた記事を書いてから時間が経ち、自分の意見も少し変わってきました。
この記事では「本当にプロダクションコードからletを排除し、constに置き換える必要があるのか」という根本的な部分をもう一度考えていきます。
Q. そもそもletは可読性が低いのか?
条件付きでNoといえます。以下のコードを御覧ください
/**
* 与えられた数値配列の偶数のみを合計した値を返す。
* @example sumIfEven([1, 2, 3, 4]) // -> 6
*/
export function sumIfEven(numbers: number[]) {
let ret = 0
for (const num of numbers) {
if (num % 2 === 0) {
ret += num
}
}
return ret
}
このコードにはletを使っていますし、何ならret
という命名を放棄したような変数名が使われていますが、別段読みづらくはないはずです。理由を言語化します。
- JSDocのコメントが丁寧に書かれているため、読み手が想像しやすい。
- スコープが短く、letの影響範囲が小さいため、読み手の負担が少ない。
したがって、ユーティリティ関数のようなスコープの短い場所で、コメント付きでletを使うのは全く問題ないと考えられます。
Q. それでは、スコープが長くならないように適度に関数に分割すれば、letを使いまくっても問題ないのか?
チームやプロジェクト、メンバーの得意、不得意によると考えます。
開発が進んでいくにつれて、スコープが長い関数というのが生まれそうになります。例えば、ユーザー登録フォームで登録ボタンが押された時の処理を書いた関数があるとします。開発当初は、あまり行数がないため、letを使ったとしましょう。
function onClickRegister() {
let foo = 0
// *************
// 10行のコード
// *************
}
この時点では可読性がそこまで悪くないかもしれません。しかし、後に仕様が追加され、ユーザー登録時の処理が増えたとしましょう。
function onClickRegister() {
let foo = 0
// *************
// 10行のコード
// *************
// *************
// さらに10行のコード
// *************
}
スコープが長くなって、変数foo
の影響範囲が大きくなってきました。読み手に強いる「fooの値を脳内に記憶しておく」という負担が大きくなってきます。さらに処理が追加されていくと、次第に「読みづらく、保守しづらいコード」になっていってしまいます。
別の関数に切り出せばもちろん。解決します。
function onClickRegister() {
doFoo()
doBar()
}
しかし、「切り出す」と一口に言っても考えなければならない、難しいポイントがいくつもあります。
- どのような粒度で切り出すか
- 切り出した関数の命名はどうするか
- 切り出した関数はどこに置くか
「これくらいエンジニアならできて当たり前」という意見が聞こえてきました。理想としてはそうなのですが、メンバーのスキルや、プロジェクトの納期によって、現実には難しい場面がでてくると考えています。
- 変に切り出したせいでむしろ読みづらくなる
- 納期が迫っているので、関数に切り出している余裕がない
ということが発生する開発現場も想像できます。
そのため、メンバーの得意、不得意によって以下のような方針をとるのが良いと考えています。
- mapやreduceが使える人は、積極的に使い、letを少なくしておく。
- mapやreduceが苦手な人は、letやfor文を使っても良い。ただし、なるべく関数に切り出す
Q. でも、mapとかreduce使うよりも、letとforループを使ったほうがパフォーマンスが良いのでは?
はい。しかし、データ数が少ない場合は、パフォーマンスの差は小さいです。
ユーザーが気づかないくらいのパフォーマンス向上よりも、開発者が読みやすいようにコードを書いておき、バグが発生する可能性を減らしたほうが良いと考えることもできます。そのほうが、結局のところユーザーの利益につながるというわけです。
結論
- 短いスコープだったら、letを使っても何ら問題ない
- しかし、短いスコープを維持するのが難しい状況であれば、letの使用を控えることも選択肢のひとつになる
- letを使わないとコードが書けない人は、関数に切り出す練習をするか、
map
とかを覚えるかをしたほうが良い