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

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

新規登録して質問してみよう
ただいま回答率
85.47%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

7回答

2540閲覧

無名関数の複数回実行について

ato

総合スコア9

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

3グッド

1クリップ

投稿2020/02/21 00:03

今までコードを書くとき下記のような形で無名関数を利用することが多かったのですが、
改めて調べてみると複数回実行する必要がない関数を定義するときに有用、という記事が複数でてきました。

var test = function(){ console.log('hoge') } $(window).on('resize', function () { test(); }); $(window).on('load', function () { test(); });

なんとなくの手癖でずっと無名関数を書いてそれを通常の関数のごとく利用してきたのですが、
無名関数を複数回利用することは基本的によくないのでしょうか?

kyoya0819, miyabi_pudding👍を押しています

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答7

0

ベストアンサー

プロジェクト内で統一されていれば
変数宣言に無名関数作って代入する事は問題ありません。

ただし巻き上げがあるので、プロダクト内での混在は絶対にしないように注意しましょう。
これだけが鉄の掟です。


さて、それを踏まえてどちらが良いのか?

あくまで私個人の意見としては
巻き上げはバグの温床でこそあれ、有用なシーンがありません。

もし私がプロジェクトリーダーであれば巻き上げが含まれるコードは全て破棄!
これからは無名関数を代入しましょう!

私個人の好き嫌いを語っても仕方ないので、
一般的な流れとして巻き上げを排除していく流れを2つ紹介します。

  • CoffeeScriptの実装
  • ES2015の実装

CoffeeScriptの実装

CoffeeScriptと呼ばれるAltJSの一つがあります。
これはRubyやPythonのようなイケてる構文を使ってJavaScriptを表現し、
最後のWebサーバからブラウザに読み込ませるタイミングでコンパイルすると、
素のJavaScriptのコードに生まれ変わるというものです。

このCoffeeScriptで作られたコードは
JavaScriptに置き換えた時に一貫性のある非常に美しいコードになる事が特徴で、
それをもってお行儀の良いコードと評価されていました。

ES2015以降の新JavaScriptで次々と導入されたモダンな構文でCoffeeScript別に要らんよなとなり、
TypeScriptに完全に負けてしまいましたが……

そのCoffeeScriptが採用している唯一の関数宣言がこちら!

CoffeeScript

1square = (x) -> x * x

js

1var square = function(x) { 2 return x * x; 3};

普通の関数宣言が出来ないんですね。
この辺の実装から巻き上げによる処理順の変更を嫌った事がわかります。
別にこれでも困った試し等一度もありません。

CoffeeScriptをベースに更に拡張したAltJSは色々とあり、
私はその派生の一つを愛用していますが、それも関数宣言はこれ一択です。


ES2015の実装

ES2015により従来のvar宣言の他に、2つの宣言が追加されました。

  • 再代入を禁止して可読性を高めるconst
  • 再代入を明示するlet

新しい宣言は巻き上げを行いません
これはもう巻き上げに依存した設計にするなと言っているようなものでしょう。

他にもアロー関数の実装が目玉ですよね。
もうfunctionなんて予約語は不要です。

大手のコーディングスタイルガイドなんかで明確に関数宣言やめろと言ってる記事みたいなものは見つかりませんでしたが、
PySpaという団体が下記の記事で関数宣言はconstにアロー関数を代入しようよと説いています。
イマドキのJavaScriptの書き方2018 - Qiita

アロー関数のみにしていく

functionキーワードはもう捨てましょう!functionキーワードのthisの取り扱いはトラブルの元です。もう存在しなかったものとして歴史の闇に葬ります。次の書き方は古いfunctionキーワードを使っています。こういう説明を見かけたらゴミ箱にダンクシュートします。

js

1// 古い関数定義 2function name(引数) { 3 本体 4}

今時はアロー関数を使って書いていきます。特に、今時の書き方は、JavaScriptでよく扱う、無名関数との相性が非常に高くなっています。

js

1// 今時の関数定義 2const name = (引数) => { 3 本体 4};

言いたいことが全部書いてありました。
JSのthisの扱いも非常に面倒で巻き上げと共に忌み嫌われる機能TOP2です。
ES2015で追加されたクラス構文以外、一切のthisは不要となりました。

投稿2020/02/21 02:57

miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ato

2020/02/24 23:35

すごくご丁寧にありがとうございます。 アロー関数について、あまりメリットや違いがわかっていなかったのでいつもの手癖で書くことが多かったので、これを機に違いを把握して書くようにしたいと思います。
guest

0

yambejpさんの回答が正答かと思いますが、
定義の仕方によって、性質がいくつか異なるため、その部分の回答をさせていただきます。

宣言の順番が関係あるかないか

これが一番の違いになります。
例えば、下記のような場合、宣言がないというエラーが起きます。

javascript

1hoge(); // Uncaught ReferenceError: Cannot access 'hoge' before initialization 2 3const hoge = function(){ 4 console.log('hoge'); 5};

これは、宣言子でhogeを宣言しているため、行の上から処理となるので、
hogeなんてないよ、とエラーが起きています。

しかし、下記では宣言の順番は関係なく、エラーは起きません。

javascript

1hoge(); // hoge 2 3function hoge(){ 4 console.log('hoge'); 5}

これは、JavaScriptのこの定義の仕方の最大の特徴と言えるでしょう。

再代入を不可にできるか否か

あまり意識しないことですが、下記の場合、再代入となるので、中身が変わってしまいます。

javascript

1function hoge(v){ 2 this.value = v; 3 console.log('hoge'); 4} 5hoge.prototype.echo = function(){ 6 console.log(this.value); 7} 8 9hoge = 25; 10 11const hogeInstanse = new hoge('hoge'); // Uncaught TypeError: hoge is not a constructor

対して、宣言子を使った場合、
constを使うことができるため、再代入不可にすることも可能です。

javascript

1const hoge = function(v){ 2 this.value = v; 3 console.log('hoge'); 4} 5hoge.prototype.echo = function(){ 6 console.log(this.value); 7} 8 9hoge = 25; // 再代入不可なので、ここでエラーが起きてくれる Uncaught TypeError: Assignment to constant variable. 10 11const hogeInstanse = new hoge('hoge');

何がいいかというと、こうしておけば、同じ変数名使わないで済んで未然にエラーを回避できるってわけですね。

他にも様々違いはあるかと思いますが、
ようは特徴を認識し、使い分けをするといいのではないかと思います。

今回の質問の場合は、他の回答者さんのおっしゃる通り、無名関数にする必要はないかと思います。
ですが、もしかしたら、無名関数として定義する必要がある場面もあるかもしれません。

投稿2020/02/21 02:10

miyabi_takatsuk

総合スコア9528

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyabi_takatsuk

2020/02/21 02:17

改めて見れば、エスパー回答な気がする。
ato

2020/02/24 23:45

順番…確かにそうですね。 特徴を認識し、使い分けをする、というのができてなかったのでこんな質問をしてしまったのですが、おっしゃるとおり通常の場面では使う必要はないですよね…違いを把握しつつもう少し場面を意識して組むようにします。ご回答ありがとうございました。
miyabi_takatsuk

2020/02/25 00:37

様々な物の特徴は、公式ドキュメントを見るのが一番早いのですが、 それでもわからない時は、実際に使っていろいろ試すといいかと。 function hoge(){}定義が、再代入可能なのは、私もやってみて初めて知りましたしね。 JSならできるべな、とは予想はしてましたがw
miyabi_takatsuk

2020/02/25 00:47

ただ、miyabi-sunさんの回答の通り、 functionキーワードは今後捨てるべきと私も思います。 IEはアロー関数や、クラス構文などに対応していないので、 IE対応はBabelなどのコンパイラーを使うといいかと。 jQueryを使う際も注意が必要ですが、jQuery自体もそのうちアロー関数対応とかも進むかと思います。
guest

0

ES2015+で書くのであれば、その結論に至るまでの考察は長くなるのでここでは省かせていただきますが、私はconst宣言初期化子アロー関数const f = () => {};を推奨します。


なお、ES5の場合は、**代入によって関数に名前が設定されないため、**質問にあるような書き方は推奨しません。(そもそも、ES5を使い続けることを推奨しません。)

投稿2020/02/21 13:54

raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ato

2020/02/24 23:58

アロー関数、存在は知っていてES2015での書き方にも慣れないとな、と思いつつ、つい業務で急いでいると慣れた書き方でやってしまいがちで…とはいえ、推奨されない書き方だと思ってもなかったので、これを機にアロー関数での書き方に変えようと思います。ご確認ありがとうございました。
guest

0

別に大した違いがないので、問題ないと思います。

js

1 var test = function(){}; 2 test.name; // "test"

js

1 function test(){}; 2 test.name; // "test"

投稿2020/02/21 04:33

編集2020/02/21 05:17
Lhankor_Mhy

総合スコア36134

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ato

2020/02/24 23:56

確かに結果だけ見ると違いはないのですけどね…他の方もおっしゃるとおり絶対NGではないですが、場面によって使い分ける、意味を理解する、というのが大事なんだな、と改めて勉強になりました。ご回答ありがとうございました。
guest

0

このサンプルの通りであれば次のように書けば良いのでは?

JavaScript

1var test = function() { 2 console.log('hoge') 3} 4$(window).on('resize', test); 5$(window).on('load', test);

投稿2020/02/21 00:14

hoshi-takanori

総合スコア7895

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ato

2020/02/24 23:50

たしかにそれもそうですね…! 最初jQueryからjavascriptに触り始めたのでつい『おまじない』的に$(window).on('resize', function(){});で中に処理を書くことが多く、、あまり意識していないところでした。ご確認ありがとうございました。
guest

0

良くないとは言わないですが、「無名」である意味があまりないですよね
function test(){
console.log('hoge')
}
と、普通に関数をつくればいいので。

投稿2020/02/21 00:11

yambejp

総合スコア114883

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yambejp

2020/02/21 00:13

変数に無名関数を受ける場合、その関数の実行は宣言より後ろでないといけません。 通常の関数だとそのスコープの内で宣言さえすれば、前でも動作します
yambejp

2020/02/21 00:19

try{ test1(); const test1=()=>{ console.log(1); }; }catch(err){ console.log(err); } try{ test2(); function test2(){ console.log(2); }; }catch(err){ console.log(err); }
maisumakun

2020/02/21 04:42

> その関数の実行は宣言より後ろでないといけません。 相互再帰や非同期処理など、「代入後のタイミング」で実行される場合、ソースコードでの位置は上にあっても構いません。
yambejp

2020/02/21 04:58 編集

> 「代入後のタイミング」で実行 まぁ確かにそうですね、表現がまちがっていました try{ setTimeout(()=>test3(),1); const test3=()=>console.log(3); }catch(err){ console.log(err); } ※上記あとから後で宣言してますが結局スコープは違います try{ setTimeout(fn=>fn(),1,test4); const test4=()=>console.log(4); }catch(err){ console.log(err); } ※上記test4はやはり先に宣言されてないのでだめです
ato

2020/02/24 23:52

おっしゃるとおりですね…わざわざ無名関数にする必要無い場面で使ってしまっていたのでもうちょっと意味を理解して使うようにしたいと思います。順番やコードの例もありがとうございます!とてもわかり易くて助かりました。
guest

0

共通の処理を関数化する理由がほぼメンテナンス的な理由なので。

同じ無名関数を書いてある部分が明らかに同じ処理を行うことがわかっているなら
関数定義化しておいた方が後々修正が必要になった時に楽だし、
修正忘れが発生することでバグの温床になることを防ぐことができます。

投稿2020/02/21 00:19

編集2020/02/21 00:20
yureighost

総合スコア2183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ato

2020/02/24 23:48

バグの温床と言われると、今まで書いてきたコードを思うとちょっと申し訳なくなりますね。。 改めてコードを見直すいい機会になりました。ご確認ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問