[解決済み】JavaScriptのクロージャと無名関数の比較
質問
現在、友人とJSにおけるクロージャとは何か、何がクロージャでないのかについて議論しています。私たちは、それを本当に正しく理解しているかどうかを確認したいのです。
この例で考えてみよう。カウントループがあり、コンソールにカウンタ変数を遅延して表示したい。そのため、次のようにします。
setTimeout
と
クロージャ
を使用して、カウンタ変数の値を捕捉し、値NのN倍を表示しないことを確認します。
を使用しない間違った解決方法です。 クロージャ に近いもの、あるいは クロージャ となります。
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
の 10 倍の値を出力します。
i
ループの後、すなわち10。
つまり、彼の試みは
for(var i = 0; i < 10; i++) {
(function(){
var i2 = i;
setTimeout(function(){
console.log(i2);
}, 1000)
})();
}
0から9を期待通りに印刷する。
を使っていないことを伝えました。
クロージャ
を捕捉するために
i
と言い張るが 私は、彼が使っていないことを証明しました。
クロージャ
for ループの本体を別の
setTimeout
(自分の無名関数を
setTimeout
を印刷し、また10×10を印刷する。この関数を
var
を実行し
後
のループを実行すると、10×10も表示されます。つまり、私の主張は
彼は本当に
キャプチャー
の価値
i
ということで、彼のバージョンは
ではなく
クロージャである。
私の試みは
for(var i = 0; i < 10; i++) {
setTimeout((function(i2){
return function() {
console.log(i2);
}
})(i), 1000);
}
そこで、次のようにキャプチャします。
i
(名前
i2
をクロージャ内で使用することができますが、今度は
戻る
を別の関数にして、これを渡しています。
私の場合、setTimeoutに渡された関数は、本当にキャプチャしています。
i
.
さて、クロージャを使っている人と使っていない人。
どちらの解決策も0から9をコンソールに遅延して表示するので、元の問題を解決していることに注意してください。 クロージャを使用する を実行します。
どのように解決するの?
編集後記 JavaScriptのすべての関数はクロージャであることを、次のように説明しています。 ポスト . しかし、我々はこれらの関数のサブセットを識別することにのみ興味があります。 面白い 理論的な観点から 以後、この言葉への言及は クロージャ は、特に断らない限り、この関数のサブセットを指すことになる。
クロージャについて簡単に説明します。
- ある関数を取り上げる。それをFと呼ぶことにしましょう。
- Fの変数をすべて列挙せよ。
-
変数には2つのタイプがあります。
- ローカル変数(束縛変数)
- 非局所変数(自由変数)
- もしFが自由変数を持たなければ、クロージャにはなり得ない。
-
Fが自由変数を持つ場合(これは
a
の親スコープ)であれば
- Fの親スコープは1つだけで、その親スコープには a 自由変数が束縛されている。
- もしFが 参照 の外から その の親スコープのクロージャになります。 あれ 自由変数
- その 自由変数をクロージャFのアップバリューと呼びます。
さて、これを使ってクロージャを使う人と使わない人を考えてみましょう(説明のために関数の名前をつけています)。
ケース1:友人のプログラム
for (var i = 0; i < 10; i++) {
(function f() {
var i2 = i;
setTimeout(function g() {
console.log(i2);
}, 1000);
})();
}
上記のプログラムでは、2つの関数があります。
f
と
g
. それらがクロージャであるかどうか見てみましょう。
について
f
:
-
変数をリストアップします。
-
i2
は ローカル 変数を使用します。 -
i
は フリー 変数を使用します。 -
setTimeout
は フリー 変数を使用します。 -
g
は ローカル 変数を使用します。 -
console
は フリー 変数を使用します。
-
-
各フリー変数が束縛されている親スコープを検索します。
-
i
は バウンド をグローバルスコープに追加します。 -
setTimeout
は バウンド をグローバルスコープに追加します。 -
console
は バウンド をグローバルスコープに追加します。
-
-
どのスコープにある関数か
参照
? は
グローバルスコープ
.
-
したがって
i
は 以上閉じた によってf
. -
したがって
setTimeout
は 以上閉じた によってf
. -
したがって
console
は 以上閉じた によってf
.
-
したがって
このように、関数
f
はクロージャではありません。
については
g
:
-
変数をリストアップします。
-
console
は フリー 変数を使用します。 -
i2
は フリー 変数を使用します。
-
-
各フリー変数が束縛されている親スコープを検索します。
-
console
は バウンド をグローバルスコープに追加します。 -
i2
は バウンド のスコープにf
.
-
-
どのスコープにある関数か
参照
? は
のスコープが
setTimeout
.-
したがって
console
は 以上閉じた によってg
. -
したがって
i2
は 以上閉じた によってg
.
-
したがって
このように、関数
g
は自由変数
i2
(の上位値である)。
g
)
いつ
それは
参照
から
setTimeout
.
体に悪い あなたの友人はクロージャを使用しています。内側の関数はクロージャです。
ケース2:あなたのプログラム
for (var i = 0; i < 10; i++) {
setTimeout((function f(i2) {
return function g() {
console.log(i2);
};
})(i), 1000);
}
上記のプログラムでは、2つの関数があります。
f
と
g
. それらがクロージャであるかどうか見てみましょう。
について
f
:
-
変数をリストアップします。
-
i2
は ローカル 変数を使用します。 -
g
は ローカル 変数を使用します。 -
console
は フリー 変数を使用します。
-
-
各フリー変数が束縛されている親スコープを検索します。
-
console
は バウンド をグローバルスコープに追加します。
-
-
どのスコープにある関数か
参照
? は
グローバルスコープ
.
-
したがって
console
は 以上閉じた によってf
.
-
したがって
このように、関数
f
はクロージャではありません。
については
g
:
-
変数をリストアップします。
-
console
は フリー 変数を使用します。 -
i2
は フリー 変数を使用します。
-
-
各フリー変数が束縛されている親スコープを検索します。
-
console
は バウンド をグローバルスコープに追加します。 -
i2
は バウンド のスコープにf
.
-
-
どのスコープにある関数か
参照
? は
のスコープが
setTimeout
.-
したがって
console
は 以上閉じた によってg
. -
したがって
i2
は 以上閉じた によってg
.
-
したがって
このように、関数
g
は自由変数
i2
(の上位値である)。
g
)
いつ
それは
参照
から
setTimeout
.
よかったね。 クロージャを使用していますね。内側の関数はクロージャです
つまり、あなたもあなたの友人もクロージャを使っているのです。言い争いはやめましょう。クロージャの概念とその見分け方について、お二人の理解を深めることができたと思います。
編集する。 なぜすべての関数がクロージャなのか、簡単な説明です(@Peter 氏に感謝)。
まず、次のようなプログラムを考えてみよう。 制御 ):
lexicalScope();
function lexicalScope() {
var message = "This is the control. You should be able to see this message being alerted.";
regularFunction();
function regularFunction() {
alert(eval("message"));
}
}
-
の両方がわかっています。
lexicalScope
とregularFunction
はクロージャではありません 上記の定義から . -
プログラムを実行すると
期待すること
message
を警告する なぜならregularFunction
はクロージャではない(つまり,クロージャにアクセスできるのは すべて を含む親スコープにある変数message
). -
プログラムを実行すると
を観察することができます。
その
message
は確かにアラートされています。
次に、次のようなプログラムを考えてみましょう(このプログラムは 代替 ):
var closureFunction = lexicalScope();
closureFunction();
function lexicalScope() {
var message = "This is the alternative. If you see this message being alerted then in means that every function in JavaScript is a closure.";
return function closureFunction() {
alert(eval("message"));
};
}
-
だけであることがわかっています。
closureFunction
はクロージャである 上記の定義から . -
プログラムを実行すると
期待すること
message
ノーアラート なぜならclosureFunction
はクロージャである(つまり,そのすべての 非ローカル変数 で 関数が作成されたとき ( この回答を見る ) - これは含まれません。message
). -
プログラムを実行すると
を観察することができます。
その
message
が実際にアラートされている。
ここから何が推測されるでしょうか。
- JavaScriptのインタプリタは、クロージャを他の関数と区別して扱いません。
- すべての関数は スコープチェーン を使用します。クロージャには セパレート 参照する環境です。
- クロージャは他の関数と同じようなものです。ただ、クロージャと呼ぶのは、それが 参照 スコープ内の 外部 所属するスコープ というのも というのは興味深いケースです。
関連
-
[解決済み】未定義のプロパティ 'bind' を読み込めない。React.js【重複あり
-
[解決済み】React-Routerの子が1つしかない。
-
[解決済み] JavaScriptで "use strict "は何をするのか、その根拠は?
-
[解決済み] JavaScriptで文字列が部分文字列を含むかどうかを確認する方法は?
-
[解決済み] あるJavaScriptファイルを他のJavaScriptファイルにインクルードするにはどうすればよいですか?
-
[解決済み] JavaScriptでオブジェクトをディープクローンする最も効率的な方法は何ですか?
-
[解決済み] ループ内のJavaScriptクロージャ - シンプルな実用例
-
[解決済み】JavaScriptの比較では、どちらの等号演算子(== vs ===)を使うべきですか?
-
[解決済み】JavaScriptで文字列の出現箇所をすべて置換する方法
-
[解決済み】オブジェクトからプロパティを削除する(JavaScript)
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】jquery $.ajaxオブジェクトのresponseJSONプロパティを取得する方法 [重複]。
-
[解決済み】JavaScriptのgetElementByNameが機能しない
-
[解決済み】Javascriptのコールバック関数がFirefoxで「Callback is not a function」というエラーを投げる
-
[解決済み】未定義のプロパティ 'bind' を読み込めない。React.js【重複あり
-
[解決済み】Kendo Observable Bindingと併用する場合、Kendo Switch Labelsを変更することは可能ですか?[Kendo-UI]です。
-
[解決済み】'useState' が定義されていない no-undef React
-
[解決済み】JavaScriptで関数が存在するかどうかを確認する方法は?
-
[解決済み】react router v^4.0.0 Uncaught TypeError: 未定義のプロパティ'location'を読み取れない
-
[解決済み] モディファイドクロージャーへのアクセス
-
[解決済み] クロージャ」と「ラムダ」の違いは何ですか?