- ベストアンサー
(hoge)("fuge") はどういう意味を持つのでしょうか?
- JavaScriptの匿名関数と(function(){ ... })();について理解し、下記のコードを読み解くことができました。
- しかし、(hoge)("fuge")の意味が分かりません。C言語の関数へのポインタを使った関数呼び出しに似ているという情報がありますが、具体的な理解に至っていません。
- JavaScriptポインタに関する情報を探しても、マウスポインタに関する情報ばかりで役に立ちません。ヒントやアドバイスはありますか?
- みんなの回答 (9)
- 専門家の回答
質問者が選んだベストアンサー
ほかの方もおっしゃっているように、意味無いです。あってもいいです。 ただ、私の見解としては、説明を進める上での便宜でしょう。 ブログのほうを見ると、コードが3つかかれていましたね。 1つめから3つめへ変形する過程を述べるため、強調したかったのでしょう。 >しかし、これを匿名関数にすると括弧なしでは "syntax error" が返ってきます。 function(str){} と ("fuge")即ち文字列"fuge" は各々オブジェクト同士ですので、 間には演算子やらなにやら入れてくれ。といっているのでしょう。 ブラウザが、記号だらけでどこで区切ったら作者の意図どおりになるのだろうか?と悩んでいるのです。 [1,2].toString() が正常に動作して(Arrayオブジェクト) /123/.toString() も正常に動作して(RegExpオブジェクト) {x:1}.toString() はエラーになる(object) ({x:1}).toString()は正常 このことも考えると、ブラウザにとって{}の記号は扱いづらいのでしょう。 ですから()で括り、一度コードを切ってあげる必要があります。 >() の方は動作しません。() は eval() と等価ではないようです。 evalは関数で、引数が文字列ならそれを解釈して実行、というのが本来の?目的ですからねぇ・・・ 因に、()を使うと、()の中の内容を確定してから、という手順が踏まれますので、 “人間が見る限りでは”、 hoge("fuge"); はhogeという関数を実行。 (hoge)("fuge"); はhogeという変数に代入されている、functionへの参照を取得し、実行。 といった感じでしょうね。(「参照渡し」はご存知でしょうか?) (a?hoge:piyo)("引数") といった表記ですと、?:の二項演算を“実行した結果”の参照を返し、その関数を展開、となります。
その他の回答 (8)
- Werner
- ベストアンサー率53% (395/735)
今回の件に関しては、 No.8の「関数宣言」と「関数式」という考え方が一番すっきり理解できると思います。 > var v = (function (f) f) (function (g) g) (function (n) n) (1); これは、 > var v = (function (f) f) (function (g) g) (function (n) n) (1); 1. function (f) f が、引数 function (g) g をリターン > var v = (function (g) g) (function (n) n) (1); 2. 1でリターンされた function (g) g が、引数 function (n) n をリターン > var v = (function (n) n) (1); 3. 2でリターンされた function (n) n が、引数 1 をリターン > var v = 1; という順序で処理されていると思います。 fやgが関数オブジェクトなので算術演算の対象には出来ないのでしょう。 無理矢理alertでも入れてみれば、fやgが何かは分かると思います。 var v = (function (f) (alert("f="+f),f)) (function (g) (alert("g="+g),g)) (function (n) (alert("n="+n),n)) (1); また、かっこで優先順位を変えれば期待している結果になると思います。 var v = (function (f) f*2) ((function (g) g/3) ((function (n) n) (1))); > 過去、別の質問でWernerさんに回答していただいたのを覚えています。 そのときの人だと気づいてませんでした^^; そのときに、functionオブジェクトがプロパティ持てる話はしてましたね。
- Chaire
- ベストアンサー率60% (79/130)
ECMAScript 関数で大切なのは「関数宣言」と「関数式」の区別です。無名(匿名)であることに本質的な意味はありません。 ・関数宣言は必ず名前付きであり、関数内を順次実行していく前に実体化されます。従って、ソース内の記述位置に左右されません。 ・関数式は名前付きでも名前なしでも構いません(名前付きの場合、名前の有効範囲はその関数式内に限られます)。関数式は関数内の順次実行時に評価されるため、ソースの記述位置に左右されます。 名前付き関数式は再帰で利用することが多いでしょう。また、Mozilla はデバッグのために全ての関数式に名前を付けることを勧めています。一方、Safari 2 以前は名前付きの関数式をエラーにしてしまいます。Web ページでレガシーコードを書く場合は注意して下さい。 ---- さて、下記は「意図通り」に動きません。 function f (n) { alert(n); } (1); 理由は簡単で、function というキーワードが文頭にあるこの部分は関数宣言だからです。関数宣言は順次実行の前に実体化されるので、順次実行時にここにあるのは (1) という数値だけです。 そこで、function が文頭に来ないよう (...) で括ります。 (function f (n) { alert(n); }) (1); こうすれば関数宣言と間違われず、関数式として「意図通りに」動作します。 ---- ちなみに、関数式は変数に代入することもできます。 var f = function f (n) { alert(n); }; 関数式はその場で実行できますから、 var v = function f (n) { return n; } (1); と書けば、関数式の戻り値が v に代入されます。この場合、function キーワードが文頭になく、関数式であることが明らかですから、(...) で括る必要はありません。もちろん、括っても害はありません。 var v = (function f (n) { return n; }) (1); ---- なお、JavaScript 1.8 は上記を var v = (function (n) n) (1); と書くこともできます。 var v = (function (f) f) (function (g) g) (function (n) n) (1); のような合成関数も簡潔に書けます。
お礼
回答ありがとうございます。 「関数式」がキーワードになるのですね。 関数宣言の認識はありましたが、関数式という捉え方をしていませんでした。 その認識を持ってみると、 function f (n) { alert(n); } は関数宣言だから、関数式にするために括弧で括るという仕組みがよくわかります。 > var v = function f (n) { return n; } (1); この書き方も「なるほど」と思いました。 クロージャ学習の際にこの書き方を学びましたが、戻り値が数値でも問題ないのですね。 > var v = (function (n) n) (1); この件、MDCに詳しく書かれていました。「return だけの関数」で使える省略記法なのですね。 New in JavaScript 1.8 - MDC https://developer.mozilla.org/ja/New_in_JavaScript_1.8 > var v = (function (f) f) (function (g) g) (function (n) n) (1); ちょっと自信がないのですが、 1. 最右にある関数の返り値を「左隣の関数の引数」に代入 2. 1個左にシフトして 1. を繰り返し という理解でいいでしょうか? 実際にコードテストできる環境があれば試してみるのですが、挙動を確かめるために var v = (function (f) f*2) (function (g) g/3) (function (n) n) (1); console.log(v); を実行すると、TypeErrorが出てしまって手詰まりの状態です。(Firefox3.0.1.1) 何か根本的に書き方が間違っているのかもしれません…。
- Werner
- ベストアンサー率53% (395/735)
ポインタも参照も、「オブジェクトを指し示すもの」という意味では同じです。 違いは(C言語のポインタとの比較で)、ポインタはポインタ演算が出来るとか、 一般的には参照の方が安全とされているとかそんなところかな。 参照の概念が理解できるなら、とりあえず同じものと思っていても 当面大きな問題はないと思います。 実際、ポインタは(JavaScriptのではなくもっと広義の)参照の一種といえなくもないので。 > 「関数ポインタはJavaScriptでは使わない」ということでした。(#2) 少なくともポインタという用語をJavaScriptでは普通使いませんし、 また使わなくてもJavaScriptの説明はできます。 「忘れた方が良い」と言ったのは、 C言語のポインタを知っているならまだしも 知らない人にわざわざポインタという用語を持ち出すのは 混乱させるだけでJavaScriptの理解の妨げになりそうだと思ったからです。 今回の匿名関数(無名関数)の話も、 関数ポインタが代入されているといるのではなく 関数オブジェクトが代入されていると考えた方が JavaScriptを理解する上では良いと思います。 (というか、実際その通りなので。) JavaScriptにおいては、関数がオブジェクトの一種だから、 関数を変数に代入することが自由に出来るわけです。 そして、JavaScriptのオブジェクトは関数オブジェクトも含め全て参照型で、 オブジェクトが代入されているというのは オブジェクトへの参照が代入されていることと同じです。 まあ匿名関数の話だけなら、 参照型と基本型の違いはあまり意識しなくても良さそうなので、 その辺のことは後々理解できればよいと思います。 余談。関数はオブジェクトなのでプロパティを持てます。 ------ var hoge = function(str){ alert(str); }; hoge.fuga = "hello"; //関数オブジェクトhogeのfugaプロパティに代入 alert(hoge.fuga); ------
お礼
アドバイスありがとうございます。 お礼が遅れまして、申し訳ありません。 > 参照の概念が理解できるなら、とりあえず同じものと思っていても当面大きな問題はないと思います。 > 実際、ポインタは(JavaScriptのではなくもっと広義の)参照の一種といえなくもないので。 なるほど。 とりあえず、「ポインタは参照の一種」という認識でいようと思います。 > 関数オブジェクトが代入されていると考えた方がJavaScriptを理解する上では良いと思います。 「関数オブジェクトが代入されている」という考え方でいいのですね。 いろんな考え方があって勉強になります。 > 余談。関数はオブジェクトなのでプロパティを持てます。 過去、別の質問でWernerさんに回答していただいたのを覚えています。 その節はお世話になりました。m(_ _)m
- tama_zou
- ベストアンサー率57% (4/7)
(hoge)("fuge"); は hoge("fuge"); と全く同じ意味です。hogeを両端の()は無意味です。 ( function(str){ alert(str); } )("fuge"); と書いた場合の function(str){ alert(str); } の両端の()は、()内が匿名関数であることを示すために必要ですが、 変数hogeは、匿名関数を指すポインタ(つまり関数ポインタ)として定義したので、hogeは()で囲まなくても、実行系はhogeが匿名関数を指すポインタであることを判断できます。 関数ポインタは、文字通り、ある関数を指し示しており、その関数ポインタに命名した変数名(この場合はhoge)は、あたかもポインタが指し示す関数の名前であるかのように使うことができます。
お礼
> 変数hogeは、匿名関数を指すポインタ(つまり関数ポインタ)として定義したので、hogeは()で囲まなくても、実行系はhogeが匿名関数を指すポインタであることを判断できます。 申し訳ありませんが、ポインタの定義がわかりませんでした。 ポインタを「参照」に置き換えると理解できるような気がしますが、調べてみると参照とポインタは微妙に違うようです。 また、Wernerさんの説明によると「関数ポインタはJavaScriptでは使わない」ということでした。(#2) その違いも気になります。 詳しくはわかりませんが、今までの皆さんの説明と合わせて理解が深まりそうです。 ご回答ありがとうございました。
- Werner
- ベストアンサー率53% (395/735)
> この匿名関数で「なぜfunctionを括弧で括るのか?」を理解したいと考えています。 そうしないと構文エラーになって動かないからです。 なぜ構文エラーになるのかは、 JavaScriptの構文解析の流れを追えば分かるとは思いますが、 私はそこまでしていないので知りません。 なのでこれは「例えば」ですが、かっこを消して、 function(str){ alert(str); }("fuge"); と書いたときに、構文解析で function(str)( { alert(str); }("fuge") ); という風に解釈されたとしたら、これがおかしいと言うことは分かりますよね? そういうわけで、かっこが必要になります。 ここでのかっこの意味は、 a+b*c で足し算を優先させるために付ける (a+b)*c と同じです。 四則演算でも、 var x = a+b; var y = x*c; を var y = a+b*c; と書き換えられないでしょ?(構文エラーにこそならないけど意味が変わる。) この場合、(x)*cみたいなかっこは必要じゃないけど、 (a+b)*cのかっこは意図通りの処理をさせるためには必須です。 > ------ > var hoge = function(str){ alert(str); }; > hoge("fuge"); > ------ > ならわかります。 > ここから匿名関数と同じ振る舞いにするために、 もしかしたら誤解があるかもしれないので言っておくと、 hogeの右辺にあるのも匿名関数ですよ。 > 1. () は eval() と似たような動作をする これは似てないですね。 置き換えられる方が珍しいです。 あと、匿名関数であることを区別しやすくするために 匿名関数が入った変数をかっこで囲むという話は私は聞かないですね。 というか、区別する必要性を感じた事がないので、 どういうときに区別する必要があるのかちょっと気になります。
お礼
> function(str)( { alert(str); }("fuge") ); > という風に解釈されたとしたら、これがおかしいと言うことは分かりますよね? 解析範囲が期待より長く認識されてしまう可能性があるのですね。 > (a+b)*cのかっこは意図通りの処理をさせるためには必須です。 「括弧内の式を先に評価する」あるいは「優先して評価する」という感じでしょうか。 > もしかしたら誤解があるかもしれないので言っておくと、 > hogeの右辺にあるのも匿名関数ですよ。 ご指摘ありがとうございます。 右辺が匿名関数という認識はあったのですが、「変数定義せずに匿名関数を使う」ことが先に立って不十分な説明になっていました。 > > 1. () は eval() と似たような動作をする > これは似てないですね。 やはり、eval() は無関係なのですね。失礼しました。 ここまで説明を受けてきて、ようやく納得ができた気がします。 また、私が質問当初の時点で大きな誤解をしていたことにも気が付きました。 私は (function(){ alert('Hello'); }) が関数定義で (); が関数の呼び出しだと認識していました。 だから、 ---- (function(){ alert('Hello'); }) var test = 'hoge'; (); ---- は動くはずだ、と考えていました。(これは間違いなので、上のコードは動作しません) 結論としては、 (function(){ alert('Hello'); })(); は test(); と同じ構文で、(function(){ alert('Hello'); }) が test と同じ意味を持つ、と理解しました。 匿名関数はfunctionを定義すると共に参照を返して、関数呼び出しまで実行しているのですね。 おかげさまで匿名関数で使われている括弧についてよく理解できました。 ありがとうございました。
補足
質問を締め切るのが遅れて大変申し訳ありませんでした。 今更、言っても言い訳にしかならないのですが、 多くの回答を頂いて解決した状況でベストアンサーをどなたにするかで悩んでしまい、その内別件で忙しくなってしまって忘れてしまっていました。 逆に私が回答する立場だったら、非常に困惑したと思います。 改めて、申し訳ありませんでした。 ベストアンサーは私がこの問題を考えるに当たって大きなヒントを下さった15mmさんに差し上げたいと思います。 Wernerさんに時点のポイントをと思いましたが、いつの間にかシステムが変わっていたのですね。 私がもたもたしていたせいで申し訳ないです。 何度も回答いただき、ありがとうございました。とても感謝しています。
- aigaion
- ベストアンサー率47% (287/608)
作者が意図しているかどうかはわかりませんが ()を付けて呼び出すことで hoge が匿名関数?として定義されていることを識別しやすくなります。 普通に定義された関数には()を付けない、匿名関数として変数に代入されたものは()を付ける。 などとルールを付けることでプログラムの可読性があがります。 構文的にはあってもなくても同じ意味になるので、このあたりは好みの問題ですね。 個人的は変数に代入して呼び出すなんてやり方は匿名関数の例としてどうかと思いますがね。
お礼
アドバイスありがとうございます。 > ()を付けて呼び出すことで hoge が匿名関数?として定義されていることを識別しやすくなります。 確かに、(hoge)("fuge"); に関しては括弧の有無は動作に影響しないようです。 括弧のない構文の動作は私も理解できます。 しかし、これを匿名関数にすると括弧なしでは "syntax error" が返ってきます。(Firebugで確認) ----- function(str){ alert(str); }("fuge"); ----- なので、括弧に何らかの意味があるのではないかと考えたのですが…。
補足
2つ関連性が高いのではないかと思われる情報があります。 1. () は eval() と似たような動作をする 下記URLを見ると、「() は eval() と同じ動作をする」というニュアンスの説明があり、 実際にテストしてみると確かに動作します。 ----- eval(function(str){ alert(str); })("fuge"); ----- JavaScript講座 : 関数の定義 http://www.openspc2.org/JavaScript/kouza2007/function/definition/index.html では、下記コードをテストしてみると、 ----- eval("var test='hello'; alert(test)"); ("var test='hello'; alert(test)"); // eval() と同じ動作を期待 ----- () の方は動作しません。() は eval() と等価ではないようです。 2. 式として関数を使用する 下記URLの「リスト 8. 式として関数を使用する」が目的の情報な気がしますが、今ひとつ理解できません。 「理屈はわかりませんが、関数名に括弧を付けても動きます」な理解度です…。 エレガントな JavaScript を作成するための関数型プログラミングの使用 http://www.ibm.com/developerworks/jp/web/library/wa-javascript.html
- Werner
- ベストアンサー率53% (395/735)
> (hoge) の意味がわかりません。 hoge と同じ。かっこは冗長なのでその例ではあってもなくても良いです。 関数ポインタなんて言葉はJavaScriptでは使わないので忘れましょう。 理解の助けにならないです。 hogeに入っている(hogeが参照している)のは関数と理解できていればよいです。 JavaScriptでは関数もオブジェクトの一種なので、 関数オブジェクトと呼ぶこともあります。 オブジェクトなので、他のオブジェクトと同じように変数に代入することが出来るわけです。 (この関数オブジェクトの性質がCの関数ポインタに似ているので引き合いに出されたのでしょうが、 相違点の方が多いと思うのでCを知らないなら気にしてもしょうがないです。)
お礼
アドバイスありがとうございます。 C言語の関数ポインタについても調べてみましたが、そこまで理解しなくていいと知り、少し安心しました。 別方面から模索してみます。
補足
言葉足らずで申し訳ありません。 私の質問が不十分なために意図が正確に伝わっていませんでした。 > 関数ポインタなんて言葉はJavaScriptでは使わないので忘れましょう。 なるほど。言葉の綾というか、例えだったのですね。 関数ポインタは忘れます。 > オブジェクトなので、他のオブジェクトと同じように変数に代入することが出来るわけです。 関数オブジェクトを変数に代入するところまではわかります。つまり、 ------ var hoge = function(str){ alert(str); }; hoge("fuge"); ------ ならわかります。 ここから匿名関数と同じ振る舞いにするために、 ------- var hoge = function(str){ alert(str); }; (hoge)("fuge"); ------- とし、次に ------- (function(str){ alert(str); })("fuge"); ------- と変化する。 この匿名関数で「なぜfunctionを括弧で括るのか?」を理解したいと考えています。 括弧の意味が知りたいのです。 どういう意味を持つのでしょうか?
- salonpath
- ベストアンサー率48% (194/399)
hogeやfugeに意味はありません http://ja.wikipedia.org/wiki/Hoge [引用] サンプルプログラムなどで意味のない名前が必要な場合に利用される、「意味のない名前」であることが広く知られた識別子のことである
お礼
アドバイス有り難うございます。 そして、すみません。 質問が適切ではありませんでした。 hogeやfugeが任意の名前であることは承知しています。 問題にしているのは括弧で括っていることです。 「なぜ括弧で括るのか?」「括弧にはどういう意味があるのか?」 そこのところを理解したいと思っています。
お礼
> 1つめから3つめへ変形する過程を述べるため、強調したかったのでしょう。 確かにそうですね。 私も2つめのコードがあったおかげで、匿名関数についてのヒントを得ることが出来ました。 > ({x:1}).toString()は正常 > このことも考えると、ブラウザにとって{}の記号は扱いづらいのでしょう。 なるほど、なるほど。 そう考えると、わかるような気がします。 「"{}" の構文解析が難しいから、"()" でグループ化する」というニュアンスで理解しました。 > evalは関数で、引数が文字列ならそれを解釈して実行、というのが本来の?目的ですからねぇ・・・ やはり、eval() は代替手段ですね。同一と捉えるのは無理があるようですね。 > hoge("fuge"); はhogeという関数を実行。 > (hoge)("fuge"); はhogeという変数に代入されている、functionへの参照を取得し、実行。 「functionオブジェクトの参照を取得する」という考え方でいいのですね。 > 「参照渡し」はご存知でしょうか? JavaScriptでは「参照渡し」を意識したことがありませんが、PHPのユーザ定義関数で引数の参照渡しは覚えましたので理解できていると思います。 オブジェクトの実体ではなく、参照 (エイリアス、ショートカットのようなもの) を渡すというイメージを持っています。 > (a?hoge:piyo)("引数") > といった表記ですと、?:の二項演算を“実行した結果”の参照を返し、その関数を展開、となります。 こんな書き方もあるんですね。JavaScriptは奥が深いです。 # ところで、これは「二項演算」というのでしょうか?(私はこれを「三項演算」と認識していました。) # 調べてみると、二項か三項かは被演算子の数で決まるとあるので、"hoge" と "piyo" で2つカウントしている…のでしょうか。 実際のコードをあげながらの説明がとても理解しやすく、参考になりました。 ありがとうございます。