プロが教える店舗&オフィスのセキュリティ対策術

javascriptのクロージャが理解できずに苦しんでいます。
下記のようなコードをよく見るのですが、a()とfuncA()()が等価のようなのですが
実行するとa()の場合のみ変数の値が維持されます。
a()とfuncA()()の違いを理解したいと思っています。
どなたかどうぞよろしくお願い致します。
またクロージャのわかりやすい解説サイトなどご存知の方いらっしゃいましたら
合わせてよろしくお願い致します。

function funcA() {
var i = 10;
return function() {
i++;
alert(i);
};
};
var a = funcA();
console.log(a() === funcA()())//true
funcA()();//11
funcA()();//11
funcA()();//11
a();//11
a();//12
a();//13

A 回答 (7件)

周知だと思いますが、


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
    • good
    • 0
この回答へのお礼

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

お礼日時:2010/04/18 03:24

「クロージャ」という用語に惑わされないで下さい。

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:/ …
    • good
    • 0
この回答へのお礼

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

お礼日時:2010/04/18 03:32

むだにかいとうがおおい#5です。


ていせい。

は、10をしょきちとする、かうんた製造機だ。返り血はない。

は、10をしょきちとする、かうんた表示機製造機だ。返り血はない。
に。

以下
かうんた製造機

かうんた表示機
に。


くるしんでいるところに、めんどうにさせて、きずぐちに塩をぬってやったみたい!ごめん。
    • good
    • 0

なになに、ばぶばぶ。



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かいふやした。
    • good
    • 0
この回答へのお礼

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

お礼日時:2010/04/18 03:29

まずオブジェクトについて少し。



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オブジェクトを保持する対象はありません。関数実行後にクロージャ変数を含む参照先のメモリ領域は解放されます。

プロトタイプやクロージャを知らなくても、グローバルな変数や関数を並べるだけである程度のことはできます。
意欲的な姿勢は見習いたいですが、あまり先走り過ぎるのもどうかと。。。(苦しむのって面白い?)


*誤った解釈があればフォローお願いします。
    • good
    • 0
この回答へのお礼

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

お礼日時:2010/04/18 03:28

私はクロージャをそれほど理解しているわけではないのですが、要所要所に 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() を実行した上でオブジェクト化し、内部のクロージャも実行しています。
    • good
    • 0
この回答へのお礼

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

お礼日時:2010/04/18 03:25

わかりやすい解説サイト


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

ちがっているかも。
    • good
    • 0
この回答へのお礼

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

お礼日時:2010/04/18 03:22

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!