const 変数で関数を定義するより、昔ながらの関数ステートメントを使うべきとき|ES2015

僕たちが昔、JavaScriptを書き始めた頃、Hello Worldの関数はこういう風に書いてたはずです。

 

function helloWorld() {
  return ‘Hello World!’;
}

 

しかし、最近のクールな若者は、関数をこんな風に書くみたいです。

 

const helloWorld = () => 'Hello World';

 

この文法は、ES2015 で採用された美しい一行コードでして、アローファンクションは、ES2015で採用された最も人気のある仕様です。

 

私はアローファンクションが大好きですが、それだとしても、いまだに私は、トップレベルの関数を定義するときは、昔ながらの関数定義をします。

 

何故ならば、アンクル・ボブ・マーティンのこの言葉は、その理由を説明しているからです。

 

“読む時間と書く時間の比率は、10対1をはるかに超えています。私たちは、新しいコードを書くために、常に古いコードを読んでいます。

この比率が非常に高いので、たとえ書くのが難しくなっても、コードを読むのは簡単であってほしいのです。”

 

関数ステートメントは、関数式に比べて2つの明確な利点があります。

 

利点1 : 意図の明確性

 

1日に何千行ものコードに目を通すとき、プログラマーの意図をできるだけ早く、簡単に把握できると便利です。

 

こちらを見てください。

 

const maxNumberOfItemInCart = ...;

 

文字を全部読んでも、省略記号が関数を表しているのか、何か他の値を表しているのか、まだわかりませんよね。そうかもしれない。

 

実際はこういう風に関数は書けるわけですが、変数にも見えるわけです。

 

const maxNumberOfItemsInCart = (statusPoints) => statusPoints * 10;

 

ファンクションステートメントを使えば、そのような曖昧さはありません。

 

function maxNumberOfItemsInCart(statusPoints) {
  return statusPoints * 10;
}

 

利点2:宣言の順番=実行の順番

 

理想的には、

 

コードが実行されると予想される順序で、

 

多かれ少なかれ宣言したいですよね。

 

constキーワードで宣言された値は、実行が到達するまでアクセスできません。

 

なので、以下のコードはエラーになります。

 

sayHelloTo('Bill')

const sayHelloTo = (name) => 'Hello $(name)';

 

言い換えれば、JavaScriptは “sayHelloTo “の宣言をバインドします。

 

つまり、最初に読み込んで、その値を保持するための空間をメモリ上に作ります。

 

しかし、実行中に到達するまで、”sayHelloTo “を何も設定しません。

 

sayHelloTo “がバインドされてから、”sayHelloTo “が初期化されるまでの時間を、

 

TDZ(Temporal Dead Zone)と呼びます。

 

ES2015をブラウザで直接使用している場合は以下のコードも実際にはエラーになります。

 

if(thing) { 
  console.log(thing);
}
const thing = 'awesome thing';

 

上のコードは、”const “ではなく “var “を使って書くと、エラーは発生しません。

 

なぜなら、varはバインドされたときにundefinedとして初期化されるのに対し、

 

constはバインドされたときにまったく初期化されないからです。

 

話は変わりますが…。

 

関数文には、このTDZの問題はありません。以下は完全に有効です。

 

sayHelloTo(‘Bill’);

function sayHelloTo(name) {
  return `Hello ${name}`;
}

 

これは、関数文がバインドされた時点で、

 

つまりコードが実行される前に初期化されるからです。

 

つまり、いつ関数を宣言しても、コードの実行が始まると同時に、

 

その辞書的な範囲で利用できるようになるのです。

 

先ほど説明したように、私たちは逆さまに見えるコードを書かなければなりません。

 

最下層の機能から始めて、上に向かってやっていかなければならないのです。

 

実際に、コードの先頭にAPIのちょっとした概要を提供できたらいいと思いませんか?

 

関数文を使えば、それが可能になります。

 

この(多少作為的な)ショッピングカートモジュールを見てみましょう。

 

export {
          createCart,
       addItemToCart,
  removeItemFromCart,
        cartSubTotal,
           cartTotal,
            saveCart,
           clearCart,
}

function createCart(customerId) {...}

function isValidCustomer(customerId) {...}

function addItemToCart(item, cart) {...}

function isValidCart(cart) {...}

function isValidItem(item) {...}

 

関数式の場合は以下のようになります。

 

const _isValidCustomer = (customerId) => ...

const _isValidCart = (cart) => ...

const _isValidItem = (item) => ...

const createCart = (customerId) => ...

const addItemToCart = (item, cart) => ...

export {
          createCart,
       addItemToCart,
  removeItemFromCart,
        cartSubTotal,
           cartTotal,
            saveCart,
           clearCart,
}

 

これを、小さな内部機能をたくさん持った大きなモジュールとして想像してみてください。

 

あなたはどちらを選びますか?

 

宣言する前に何かを使うことは不自然であり、意図しない結果を招く可能性があると主張する人がいます。

 

どちらかの方法が良いというのは、それぞれの意見でしょうが、

 

しかし、私に言わせれば コードはコミュニケーションです。

 

良いコードはストーリーを語ります。

 

機械のためにコードを最適化することは、コンパイラやトランスパイラ、ミニファイアーやウグイスファイアーに任せておいて、

 

私は人間の理解のためにコードを最適化したいのです。

 

アロー関数を使うとき

 

とはいえ、繰り返しになりますが、私がアロー関数は大好きなんです。

 

私は通常、小さな関数を高次の関数に値として渡すために、アロー関数を使います。

 

例えば、プロミス、マップ、フィルター、リデュースでも矢印関数を使います。

 

最後に、それらを表すコードを少し見て、終わりにしますね。

 

const goodSingers = singers.filter((singer) => singer.name !== 'Justin Bieber');

function tonyMontana() {
  return getTheMoney()
           .then((money) => money.getThePower())
           .then((power) => power.getTheWomen());
}

 

*この記事は英語の記事でめっちゃ面白いと思ったので、それの翻訳ベースです、より詳しく読みたい方は本家の記事へどうぞ。

藤沢瞭介(Ryosuke Hujisawa)
  • りょすけと申します。18歳からプログラミングをはじめ、今はフロントエンドでReactを書いたり、AIの勉強を頑張っています。off.tokyoでは、ハイテクやガジェット、それからプログラミングに関する情報まで、エンジニアに役立つ情報を日々発信しています!

未整理記事

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です