質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.11%

JavaScript (node.js) 結局のところ関数は 関数式で書くべきか? 関数宣言で書くべきか?

受付中

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 2,607

bleurouge

score 159

JavaScriptにおいて、関数式で書くべきか、関数宣言で書くのか、おそらく正解はなく、すでに様々なところで議論されていると思いますが、みなさんはどのように書いているのでしょうか?両者の違いについての質問・回答はすでにいくつかありましたが、結局のところ2016年時点でどのような使い方が良しとされているのかについては明確ではなく、興味があったので質問してみました。

es6への対応も進み、ある程度型が絞れそうな node.js でのケースを例に書いてみました。関数式と関数宣言を混在させる気持ち悪さがありますが、関数宣言の巻き上げは利用価値ががあると思っています。

'use strict';
const zzzFunc = require('./lib/zzz');
:
function AAA() {
  // 基本的には関数式で書く
  const bbbFunc = () => {
    :
    CommonA();
    CommonB();
    :
  };
  const cccFunc = () => { ... CommonA(); ... };
  const dddFunc = () => { ... CommonB(); ... };

  // 複数の関数式内で呼ばれる関数は関数宣言にしておく
  function CommonA() { ... }
  function CommonB() { ... }

  // 実行
  bbbFunc()
  .then(cccFunc)
  .then(dddFunc)
  .then(zzzFunc)
  .catch( e => console.log('ERR: ', e) );

  /*// OR モジュールを外部から利用
  return {
    bbb : bbbFunc,
    ccc : cccFunc,
    ddd : dddFunc
  };
  *////////////
}

module.exports = AAA;
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+3

 関数宣言と関数式

関数宣言/関数式は用途に合わせて使い分けるものなので統一的にどちらを選択するか検討すべきところではないと思います。

 無名関数式と名前付き関数式

無名関数式は頻繁に使うものではないと思います。
無名関数式は Function#name がないのでデバッグ時に切り分けがやりづらくなります。
無名関数式は名前付き関数式に変更しても動く為、基本的には名前つき間数式を使うと良いと思います。

 アロー関数

アロー関数は関数スコープで this 値を束縛したくない場合において有効です。

function Hoge (x) { this.x = x; }
Hoge.prototype.foo = function foo () {
  setTimeout(() => console.log(this.x), 1000);
};

new Hoge(3).foo();

ただし、アロー関数も名前がないので必要な時だけ利用する事になります。

 更新履歴

  • 2016/04/16 22:30 名前付き関数式/アロー関数の説明を追記

Re: bleurouge さん

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/04/17 11:57

    > 匿名関数式(アロー関数式も)でもES2015からは代入文等名前が付けられそうなコンテキストであればFunction#nameは付きます。
    なるほど、と思って試してみましたが、Google Chrome と Firefox で異なる動きをしました。
    - Google Chrome も Firefox も Function#name が変更されない
    - Google Chrome は関数を参照コピーした後に元の変数 fn1, fn2 に null を代入すると TypeError (関数名がないので自然な動き)
    - Firefox は上記の null 代入後に TypeError にならないがコンソールに何も出力されない (???)
    https://jsfiddle.net/j9mt2euf/1/
    しかし、アロー関数で関数名を指定できるとなると以前の質問内容も訂正しておく必要がありそうですね…。
    https://teratail.com/questions/24115

    キャンセル

  • 2016/04/17 16:36

    Function#nameはFirefoxは未実装です。
    Chromeは52から全てのケースで対応しており、Edgeも大体OKです。
    http://kangax.github.io/compat-table/es6/#test-function_name_property

    ちなみにそのコードは自環境のFx48ではTypeErrorになりました。

    キャンセル

  • 2016/04/17 17:37

    ありがとうございます。今一度、検証し直してみます。

    キャンセル

0

私自身は基本的にCoffeeScriptしか書かないので、悩んだことはありません。CoffeeScriptでは関数宣言にあたる記法はありませんが、問題になったことはありません。そもそも、基本的にクラスを書くことになるため、クラスとは関係ない関数を単独で書くことはラムダ式扱いの無名関数を除くとほとんどありません。


私自身のことは置いておいて、2016年で最も良い方法は?と言われた場合、お勧めするのは、

TypeScriptを用いてクラスベースで書く

です。

複雑かつ大規模化する昨今のJavaScriptにおいて、TypeScriptの静的型付けはかなり大きなアドバンテージを得られます。また、基本的にクラスのメソッド(メンバー関数)として書くようにすることで、関数を単独で書くこと自体をなくします。ですので、関数宣言と関数式のどちらでもなく、クラスでのメソッドの書き方とラムダ式としての無名関数以外で関数を書くことがなくなります。

プロトタイプベースオブジェクト指向はとても柔軟で強力ですが、いかんせん使いこなすにはテクニカルな部分が多く、かなりの知識と慣れが必要になります。反面、クラスベースオブジェクト指向はデザインパターンなどの研究も進んでおり、他言語での採用が多いことから、扱いやすさと言う面では圧倒的と言っても良いでしょう。クラスベースオブジェクト指向を前面に出し、クラスを基本として記述することが最良と考えています。

JavaScriptを関数型言語として扱い、関数を主体としたいと考える人もいるでしょう。しかし、JavaScriptは関数型言語としては不十分な点が多く、扱いにくいと私は思っています。TypeScriptも関数型言語としての側面は重要視しておらず、JavaScriptに準じています。より関数型言語のように扱いたければ、PureScriptやLiveScriptの採用を検討すべきです。これらには、関数のカリー化や部分適用、関数合成、パイピング、リスト内包表記、モナド(PureScriptのみ)、そして強力なPreludeが用意されており、関数型プログラミングがとても書きやすくなっています。

ECMAScript2015でも良いのではと思うかも知れませんが、TypeScriptの静的型付けが強力なため、ECMAScript2015を使うメリットがあまりありません。Reactを使う場合はBabelを使ってJSX変換で優位というのもありましたが、TypeScriptがJSX表記に対応したため、TypeScriptを使わない理由が無くなりました。node.jsやElectronであれば、ネイティブである程度対応しているというのは確かにありますが、V8エンジンも全てに対応しているわけで無いこと、他ブラウザ向けにするときはES5に変換が必須なことを考えると、結局、TypeScriptと同じように変換は必要になったりします。そもそもその変換も多くのタスクツールが用意されているため、それほど問題になりません。また、規模が小さく、静的型付けを使うほどでも無いというのであれば、CoffeeScriptを使った方がはるかに楽です。

以上、かなり偏った意見ですが、参考になれば幸いです。なお、私自身はOpalを検討中ですが、色々とうまくいかないところがあるのでちょっと悩み中です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/04/17 00:44

    これは致し方ないところなのだと思いますが、単純にJavaScriptと言った場合でも範囲の広がりがすさまじいですね。規模を考えた場合の切り口と、さらにブラウザ向けを考える時の切り口も入れると、選択肢多彩で、適宜要件に応じて考えろ。と。トランスコンパイラもなんだか乱立で、いや最初からrubyで書いたほうがいいじゃんwみたいなものまで出てくると果たしてJavaScriptとは何であるのか、みたいな。JavaScriptを捉える視点が多様化している様でおもしろいです。素敵な回答をありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.11%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる