JavaScript (node.js) 結局のところ関数は 関数式で書くべきか? 関数宣言で書くべきか?
受付中
回答 2
投稿
- 評価
- クリップ 2
- VIEW 3,341
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ページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
+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 さん
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
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を検討中ですが、色々とうまくいかないところがあるのでちょっと悩み中です。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.33%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2016/04/16 21:45
関数宣言/関数式それぞれが何者なのか?についての解説はweb上でもかなり見かけるのですが、では、それぞれはどのように使われるべきなのか?については、まとまった解説を見たことがありません。
無名関数式については、本当にやるかどうかは別として、デバッグ時は名前付きにしておくという方法があったと思います。アロー関数については最近使ってみた程度のため実際のところよくわかりませんが、そうなのですね。ありがとうございます。
また、これは著者の考え方によるところもあるのかもしれませんが、
[O'Reilly Japan - シングルページWebアプリケーション](http://www.oreilly.co.jp/books/9784873116730/)
では、無名関数式の利用に徹していると感じました。ではなぜ、この本の著者はこの書き方をしたのか?という思想的なところについては、残念ながらよく分かりませんでした。その辺の不明瞭感を取り除きたいと思いこちらで質問しました。
▼書籍内で扱うコードです(github上に上がっているコードを拝借して)
https://github.com/inouetakuya/spa/blob/master/ch09/9.5.4-caching/lib/crud.js
2016/04/16 22:10 編集
アロー関数は this 値が束縛されない為、this 値を変更したくない場合において利用価値があります。
2016/04/16 23:05 編集
名前付き関数式が書けないCoffeeScriptは滅ぶべしということ言うことですね。わかります、わかります。激しく同意しておきます。
(えっ、自分の回答ではCoffeeScript使っているとかいってなかったかって?まぁ、あれはあれだ、きっと偽物とか、別人格とか、平行世界の俺とかそういうのに違いない)
2016/04/17 00:47
まだ、他の視点の意見も出てきそうなので、もうすこしオープンで置いておきたいと思います。
2016/04/17 04:16 編集
(匿名関数式から必ず匿名関数ができるわけではありません)
http://www.ecma-international.org/ecma-262/6.0/#sec-assignment-operators-runtime-semantics-evaluation
2016/04/17 11:57
なるほど、と思って試してみましたが、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
Chromeは52から全てのケースで対応しており、Edgeも大体OKです。
http://kangax.github.io/compat-table/es6/#test-function_name_property
ちなみにそのコードは自環境のFx48ではTypeErrorになりました。
2016/04/17 17:37