• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:javascriptのクロージャが理解できずに苦しんでいます。)

クロージャの違いを理解するための質問

このQ&Aのポイント
  • javascriptのクロージャが理解できずに苦しんでいます。
  • a()とfuncA()()の違いを理解したいと思っています。
  • クロージャの解説サイトなどご存知の方いらっしゃいましたら教えてください。

質問者が選んだベストアンサー

  • ベストアンサー
  • imq
  • ベストアンサー率72% (16/22)
回答No.2

周知だと思いますが、 JavaScriptでは関数への参照にカッコ()を付けると関数を実行します。 var foo = function() { alert('a'); }; // fooは関数への参照 foo(); // 'a'と表示 次に質問文にあるような関数(クロージャ)を返す関数の場合です。 次に↓の文です。 var a = funcA(); funcAを実行して返ってくるのはクロージャへの参照です。 funcAの内部変数iが10にリセットされ、aにはクロージャへの参照が代入されます。 また、JavaScriptでは「全ての参照がなくなるまでオブジェクト(変数・関数を含む)は存在し続ける」ので、返されるクロージャとそこで使われている変数iは、funcAの外でもaがある限りは存在し続けます。 次に↓の文です。 funcA()(); 「funcA()」はクロージャへの参照が返されるので、更にカッコを付けて「funcA()()」とすると返されたクロージャを実行することになります。 つまり、「funcAを実行して、返されたクロージャも実行する」という事を1文で済ませた表記です。 funcAを実行するため、iは10にリセットされ、クロージャの実行によってiは11になります。 次に↓の文です。 a(); これは返されたクロージャのみを実行しています。 funcA自体は実行しませんので、iはリセットされず、aを実行するたびにインクリメントしていきます。

参考URL:
http://d.hatena.ne.jp/jdg/20091020/1256042918
poyon8989
質問者

お礼

ご回答ありがとうございます。 a()とfuncA()()の違いよく分かりました! またクロージャについてもなんとなくわかってきたきがします。 どうもありがとうおございました。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (6)

  • Chaire
  • ベストアンサー率60% (79/130)
回答No.7

「クロージャ」という用語に惑わされないで下さい。ECMAScript 言語規定にこの用語は(ごく特殊な場面を除き)出てきません。JavaScript における変数の有効範囲が、結果的に「いわゆるクロージャっぽいもの」を形作るというだけで、特別な概念でも何でもありません。 まず、JavaScript の変数は、基本的に「書かれている通りに、内から外へ」です。 var GlobalVar = 'Global'; function OuterFunc() {   var OuterVar = 'Outer';   function InnerFunc() {     var InnerVar = 'Inner';     alert(InnerVar); // 'Inner'     alert(OuterVar); // 'Outer'     alert(GlobalVar); // 'Global'   }   InnerFunc();   alert(InnerVar); // error!!!   alert(OuterVar); // 'Outer'   alert(GlobalVar); // 'Global' } OuterFunc(); alert(InnerVar); // error!! alert(OuterVar); // error!! alert(GlobalVar); // 'Global' 関数 InnerFunc 内からは InnerVar、OuterVar、GlobalVar の 3 つの変数を全て利用できます。しかし、関数 OuterFunc 内から変数 InnerVar は見えませんし、グローバルコンテキストからは GlobalVar しか見えません。外側の関数は内側の変数を使えないが、内側の関数は外側の変数も見える。当たり前ですね。 次に、JavaScript の関数は何でも戻り値にできます。 function F() {   return 1;   return [1, 2];   return {a:1, b:2}; } たとえ、それが関数でも。 function OuterFunc() {   var OuterVar = 'Outer';   function InnerFunc() {     var InnerVar = 'Inner';          alert(InnerVar); // 'Inner'     alert(OuterVar); // 'Outer'   }   return InnerFunc; } var ifunc = OuterFunc(); ifunc(); 関数 OuterFunc の戻り値 ifunc は、関数 InnerFunc そのものです。ゆえに、ifunc に丸括弧 () で引数を渡せば関数として呼び出すことができます。そして、関数 InnerFunc の定義は関数 OuterFunc の中にありますから、変数 OuterVar が見える。つまり、戻り値 ifunc(その実体は InnerFunc)を媒介にして、本来外側から利用できないはずの OuterVar を制御できてしまうわけです。 あと、実行コンテキストや初期化子など細かい事項はありますが、いちいち書きません。JavaScript のいわゆるクロージャは、JavaScript の変数スコープから生まれる結果の 1 つに過ぎません。ですから「こう書くのがクロージャ」と決めつけずに、いろいろ変数の管理方法を工夫していけば、そのうち慣れると思います。 あるいは、先に再帰に慣れて下さい。その方がいわゆるクロージャを理解しやすいと思います。

参考URL:
http://web.archive.org/web/20061018193606/http://www.hawk.34sp.com/stdpls/jsnotes/jssinso/03_variable_context2.html
poyon8989
質問者

お礼

ご回答ありがとうございます。 無理に理解しようとせずにと思いつつもつい気になってしまいまして、、、。 とても参考になりました。ありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。
回答No.6

むだにかいとうがおおい#5です。 ていせい。 は、10をしょきちとする、かうんた製造機だ。返り血はない。 を は、10をしょきちとする、かうんた表示機製造機だ。返り血はない。 に。 以下 かうんた製造機 を かうんた表示機 に。 くるしんでいるところに、めんどうにさせて、きずぐちに塩をぬってやったみたい!ごめん。

全文を見る
すると、全ての回答が全文表示されます。
回答No.5

なになに、ばぶばぶ。 function funcA() {  var i = 10;  return function() {   i++;   alert(i);  }; } は、10をしょきちとする、かうんた製造機だ。返り血はない。 var a = funcA(); は、a を かうんた製造機にした。 a() === funcA()() の a()は、 aを、かうんとしてみた。けっかはなにもない。(11をひょうじしただけ) ないぶのかうんたは11となった funcA()()は、 へんすうにだいにゅうしないで、かうんた製造機をつくったけど、すぐじっこうした。 だけど11をひょうじしただけで、けっかはなにもない なにもないどうし、くらべたから、おんなじだった。 funcA()(); funcA()(); funcA()(); へんすうにだいにゅうしないで、かうんた製造機をつくったけど、すぐじっこうした。 だけど11をひょうじしただけで、けっかはなにもない。を3かいやってみた。 a();//11 <これって12からじゃない? a();//12 a();//13 aのかうんたを3かいふやした。

poyon8989
質問者

お礼

ご回答ありがとうございます。 より理解を深めるため今後何度も読み返させていただきたいと思います。 どうもありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。
  • my--
  • ベストアンサー率89% (91/102)
回答No.4

まずオブジェクトについて少し。 var obj = { pro: 1 }; objが持つ値は「参照」です。参照を値に持てば、それはオブジェクトです。 参照とはプロパティ集合のある場所(メモリ領域)を示すアドレスのようなものと考えていいと思います。 var variable = obj; variableに代入されたのは「参照」です。(参照渡し) variableはプロパティ集合(pro) へのアクセス手段を手に入れただけで、variable固有のプロパティが複製されるわけではありません。 pro(プロパティ)はobj固有のものではなく「参照固有のもの」と言えます。 これらを踏まえて console.log(a() === funcA()())//true ここで混乱してしまっているようですが var a = funcA(); var v1 = a(); var v2 = funcA()(); alert(v1); // undefined alert(v2); // undefined どちらも戻り値が同じundefinedです。意図した比較ではないですよね。 alert(a === funcA()); // false オブジェクト(参照)比較ならこのようになるかと。 funcA関数は関数式(function() { ... })から返されるFunctionオブジェクト(参照)を 戻り値(return)として返しますが、関数式はすでに変数aに代入され使用されているのと同じ参照を返すことはありません。 これはローカル変数(var宣言)も同じです。ローカル変数は関数呼出し時に初期化されます。 初期化の際、内側の関数式で使用(クロージャ)され解放されずにいるものと同じ参照が割り当てられることはありません。 funcA()()の場合、funcA関数から返されるFunctionオブジェクトを保持する対象はありません。関数実行後にクロージャ変数を含む参照先のメモリ領域は解放されます。 プロトタイプやクロージャを知らなくても、グローバルな変数や関数を並べるだけである程度のことはできます。 意欲的な姿勢は見習いたいですが、あまり先走り過ぎるのもどうかと。。。(苦しむのって面白い?) *誤った解釈があればフォローお願いします。

poyon8989
質問者

お礼

ご回答ありがとうございました。 仰るとおり先走ってます。 a(),funcA()の違いよくわかりました。 より理解できるよう何度も読み返させていただきます。 どうもありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。
  • think49
  • ベストアンサー率59% (285/482)
回答No.3

私はクロージャをそれほど理解しているわけではないのですが、要所要所に console.info() を入れるとわかりやすいんじゃないでしょうか。 function funcA() { console.info('--- funcA'); var i = 10; return function() { console.info('closure'); i++; console.log(i); } }; var a = funcA(); console.log(a() === funcA()()); //true console.log((funcA())() === funcA()()); //true funcA()(); //11 (funcA())(); // 11 a(); //11 a(); //12 a(); //13 (funcA())(); は匿名関数とよく似ています。 (function(){ // 処理 })(); は function(){ // 処理 } を括弧で括ってオブジェクト化した上で実行するわけですが、 (funcA())(); は funcA() を実行した上でオブジェクト化し、内部のクロージャも実行しています。

poyon8989
質問者

お礼

ご回答ありがとうございます。 より理解できるよう何度も読み返せさせていただきたいと思います。 ありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。
  • yyr446
  • ベストアンサー率65% (870/1330)
回答No.1

わかりやすい解説サイト http://www.atmarkit.co.jp/fdotnet/ajaxjs/ajaxjs03/ajaxjs03_04.html とか わかりにくい解説サイト(ECMA-262の邦訳) http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/15-3_Function_Objects.html#section-15.3.4.4 とか var a = funcA(); は、funcA()の結果の代入ではなくて、funcA()をインスタンス化している。 別のアドレス空間にaの名前でfuncA(クラスオブジェクト)が確保されている。だからa()を呼ぶたびにクラス内のコードが実行され保持されている。だからa();//11 a();//12 a();//13 となる。 一方 funcA()();はfuncAを直接毎回実行するわけだから、常に funcA()();//11 となる。 ちがっているかも。

poyon8989
質問者

お礼

ご回答ありがとうございます。 理解できるようサイトも参考にさせていただきます。 ありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • クロージャ

    javascriptのクロージャについて。 クロージャにnewは必要ですか? コンストラクタであれば、newでオブジェクトを生成しますが、クロージャはどうでしょうか? クロージャ-------------------- function Person(n, a){ var name = n; var age = a; return { getName: function() { return name; }, setAge: function(i){ if( 0<= i ){ age = i; } }, getAge: function(){ return age; } } } var p = new Person('Hanako', 3); // new を付けなくても生成できる

  • JavaScriptのクロージャの挙動の違い

    JavaScriptのクロージャの挙動の違い こんにちは。今現在JavaScriptの学習をしているのですが、なぜこのようにしなければならないのかがわかりません。まずはやってはいけないコード var add_handler=function(nodes){ var i; for(i=0;i<nodes.length; i+=1){ nodes[i].onclick=function(e){ alert(i); }; } }; 正しい例 var add_handler=function(nodes){ var i; for(i=0;i<nodes.length; i+=1){ nodes[i].onclick=function(i){ return function(e){ alert(i); }; }(i); } }; 本には変数のコピーしたものにアクセスしているわけではないことに注意と書いてありました。悪い例がなぜ悪いのかはなんとなく理解できました(おそらくイベントハンドラが呼ばれたときに呼び出されるからと理解)が、それの対処法として下の記述方法で解決できるのかがどうも納得できません。 よろしくお願いいたします。

  • javascriptのコードについて

    以下のコードなのですが、 エンクロージャー関数の ローカル変数hogeをさらに、エンクロージャー内部で定義された 関数ででクロージャーとして保持させたいメソッドのコードですが var Method = function (){ var hoge = "初期値"; var getter = function (){ return hoge; } var setter = function (param){ hoge = param; return hoge; } return {"set" : setter,"get" : getter} } var obj = Method(); console.log(obj); console.log(obj . get()); obj.set("初期値変更"); console.log(obj.get()); obj . set("更に変更"); console.log(obj.get()); この場合、メソッドの返り値として、一般的な文献に乗っている関数(関数オブジェクト)を返すのではなく オブジェクトリテラルとして返しています。 この場合でも、動きとしてはクロージャーの動きをしているのでhogeという変数の保持はできているっぽいんですが クロージャーって関数内で定義された関数であれば、どういう返り値の返し方でも クロージャーになるのですかね? また、この方法は、一般的にjsで関数コンストラクタ呼び出しをしてインスタンスを作る際privateメンバを実現する方法として紹介されていますが、 これはクロージャーとして生成するたんびに内部の変数を保持するためメモリ食い虫になるらしいのですが まず間違いなく、このクラス(便宜上そう呼びます)のインスタンスはひとつしかつくることはない!!という仕様だとしても いけないのでしょうか? というかもう現状javascripのバッドノウハウ的なものになっているのでしょうか?

  • thisとvar ?

    javascript初心者です。 コンストラクタ(プロトタイプ)とクロージャを学んでいますが、 コンストラクタ(プロトタイプ)では、関数内にthisで変数宣言、クロージャはvarで宣言しています。 この違いの理由は何でしょうか?漠然とした質問ですみません。 thisとvarでの変数宣言の違いなど教えていただけないでしょうか? コンストラクタ-------------------- function Person(n){ this.name = n; } Person.prototype.city = 'Tokyo'; Person.prototype.moveTo = function(c){ document.write(this.name + ': Moving to... ' + c + '<br>'); Person.prototype.city = c; } クロージャ------------------- function Person(n, a){ var name = n; var age = a; return { getName: function() { return name; }, setAge: function(i){ if( 0<= i ){ age = i; } }, getAge: function(){ return age; } } }

  • javascriptの書き方

    javascriptの記述で不明点があります function javas(){ for (var day = 1; true; day++){ //--何かしらの処理 } alert(""); //--ここが呼び出せない } 最後の部分が呼び出せないのですが、もともと呼び出せないものなのでしょうか。 もし呼び出せるとしたらどのように書けばよろしいでしょうか。 よろしくお願いします。

  • JavaScriptの変数をjavaのメソッドの引数に渡す

    JavaScriptの変数をjavaのメソッドの引数に渡すことはできますか? <%@ page contentType="text/html; charset=Windows-31J" %> <%! String chek(String a){ String st = a; return st; } %> function chek(){ var a1 = "abcd"; var a2= "<%=this.chek(%>ai<%=)%>"; alert(a2); }

  • JavaScriptの論理演算子について

    JavaScriptの論理演算子について質問です。 「alert(e.target||e.srcElement);」の結果がtrue かfalseではなくて「object HTMLHtmlElement」となるのは何故なのでしょうか? JavaScriptでは"||"は論理演算子ではないのでしょうか? ----------------------------------------------------------------------- <body> <a id="id1">Textarea</a> <script> (function () {   var i = document.getElementById ("id1");   document.addEventListener("dblclick", addEL, false);   function addEL (e) { alert(e.target||e.srcElement);   } }) (); </script> </body>

  • Javascriptのスコープについて。

    私は何か大きな勘違いをしているでしょうか? for(var i=0; i<10; i++){ for(var i=0; i<10; i++){ alert(i); } } 単純なこのサンプルで、ループは総計100回回るはずだと思うのですが、 外側ループのローカル変数であるiが、なぜか内側ループのローカル変数iと同一視され、 結果10回しか回りません。 どこがおかしいでしょうか。 比較対象として、以下を実行しました。 var i=3; (function(){ var i = 2; alert(i); })(); alert(i); 結果は2, 3となりました。 スコープは機能しています。 前者はfor文だからおかしくなったのでしょうか? ・・・while文で書き下すと、あ、for文の()中で宣言するカウンタ変数というのは、該当for文のスコープの外に出ちゃっているんですね・・・。 つまり、for(var i・・・)と宣言したカウンタ変数のスコープは、for(){}のスコープではなく、その一個外に所属すると。。 こういう解釈でよいのでしょうか?よろしくお願い致します。m(_ _)m var i=0; while(i<10){ var i=0; while(i<10){ alert(i); i++; } i++; }

  • 教えてください。javascriptで困っています

    javascript初心者です。たくさんある変数をfor文を使って計算させたいと思っています。計算はfor分の中でしたいのですが、うまくいきません。以下のようなことをしたいのですが。evalなどを使うのでしょうか。調べるキーワードがわからなかったので、投稿させていただきました。教えてください。よろしくお願いします。 var x1=1; var x2=2; var x3=3; var x4=4; var x5=5; var y1=1; var y2=2; var y3=3; var y4=4; var y5=5; var z1; var z2; var z3; var z4; var z5; for(var i=0 i<5 i++){ "z"+i = "x"+i + "y"+i; } alert(z1); alert(z2); alert(z3); alert(z4); alert(z5);

  • javascriptのレキシカルスコープについて

    JSのレキシカルスコープがわかりません。 JSだと、一番外側のスコープで var str = "一番外側のスコープ"; とすると それ以降、ユーザー定義関数の中の、いわゆる関数スコープ内でも var str; と関数内で再定義しない限り、str = "一番外側のスコープ";を つかいまわすことになりますよね? 一度、関数内で、値を変更すると親スコープでもその変更が生きたままになると思います。 var str ="一番外側のスコープ"; function test(){ alert(str); str ="値の変更"; } alert(str) // 関数内で値をかえたけど、それが親スコープにも反映されてしまう。 このことがレキシカルスコープでしょうか? ただ、どこかのサイトで var num = 100; function makefunc() { return function() { alert(num); } } function callfunc() { var num = 50; var func = makefunc(); func(); }; callfunc(); でmakefunc()関数を呼び出した際の 関数内のnumという変数の値が100になるというのです。 実際、実行するとその通りなのですが、 var num = 50; という宣言は、callfunc() という関数の関数スコープ(ローカルスコープ) というのは理解しています。 でその中のいわゆる内部関数というのでしょうか? var func = makefunc(); func(); を実行した際の 変数numというスコープが 一番最初に宣言した  var num = 100; という値をさすというのです。(※そのサイトではこれをレキシカルスコープと呼んでいました) いったどういう動作がレキシカルスコープなのでしょうか? 他にも、クロージャともごっちゃになっております。 識者の方ご教授ください。 参考元はここです http://garden-place.jp/tech/javascript/scope-chain.html