JavaScriptクロージャの詳しい解説_基礎知識

WBOY
リリース: 2016-05-16 16:16:09
オリジナル
912 人が閲覧しました

前回の記事では、事前解釈の概要を説明しましたが、このブログ記事を書く前に、これらのケースが比較的包括的であることを考慮して、このブログ記事を段階的に書いていきました。また、JavaScript の学習と理解を深めることも容易になります。

序文

同僚が面接に行ったところ、面接官が質問をしました。「締めくくりを書いて見せてもらえますか?」そこで私の同僚はすぐに次のコードを書きました:

コードをコピーします コードは次のとおりです:

関数 fn(){
alert('Hello JavaScript Closure!!!'); // しまった、E のテキストが良くないので、クロージャの単語を書き出す翻訳者を探す必要がありました
}
fn();

その後、面接官は首を振って言いました、「どうしてこれが終結と言えるのでしょうか?」 結局、二人は議論することができず、同僚は毅然として立ち去りました、面接官は一体何をしているのですか? (この物語は全くの架空のものであり、類似点がある場合は全くの偶然です)

クロージャは、多くの人にとって「ハイエンドで習得が難しい」テクノロジーである可能性があります。おそらく、多くの人にとって、それがクロージャとしてカウントされる唯一のものです。

例 1:

コードをコピーします コードは次のとおりです:

関数 fn() {
戻り関数 () {
alert('例 1');
}
}
fn()();

例 1 PS: これはあまり高度ではないようです。この人はあまり上手ではないようです。

例 2:

コードをコピーします コードは次のとおりです:

;(関数() {
alert('例 2');
})();

例 2 PS: これは前の例よりも高度に見えますが、最初の括弧の前にセミコロンが追加されています。なぜセミコロンを追加するのでしょうか? さて、この質問は今のところここに置いておいて、後で説明します。

例 3:

コードをコピーします コードは次のとおりです:

~関数 fn() {
アラート('例 3')
}();

例 3 追伸: これは最先端です。私はあまり本を読まないので、嘘は言わないでください。

私はあまり本を読まないので、この 3 種類の「クロージャ」しか書くことができません。ブロガー仲間ならもっと良い「クロージャ」を書くことができると思います。誰かが私のナンセンスをやめて研究を続けてください。関数の動作の仕組みはすでに知っています。このスコープをタイトルに追加したくないのですが、なぜですか?古い習慣、最初にコードを書く:

コードをコピーします コードは次のとおりです:

var n = 10;
関数 fn(){
アラート(n);
var n = 9;
アラート(n);
}
fn();

簡単に言うと、絵を描いて (著者は Windows に付属の描画ソフトウェアのみを使用します。より良いものがあればお勧めします)、それを分析しましょう。

分析 1

この図から、2 つのスコープがわかります。1 つはウィンドウ スコープ (トップレベル スコープ)、もう 1 つは fn が呼び出されたときに形成されるプライベート スコープです。スコープとは何ですか。スコープは実際にはコード実行環境です。たとえば、生徒の学習環境が学校であり、その生徒が非常にやんちゃで、よく夜にネットカフェに行ってゲームをしている場合、それはプライベートな環境を形成することに相当し、この範囲がその生徒の範囲となります。インターネットカフェ。よし!このクリはオナホ本人に酷似しており、「若い時に頑張らないと大人になったら叩かれるよ」と思わずため息が出てしまいました。話を戻しますが、実は関数 fn の定義はコード (図の赤枠) を指す記述であり、fn を呼び出すと (図の緑枠) 当然スコープが形成されます。このスコープでは、コードは実行前に事前解釈されますが、この fn を再度呼び出すと、実行前に事前解釈が行われて新しいスコープが形成されるとは言いません。そして、コードは最終実行後に破棄されます。

クロージャについて

関数が呼び出されるとプライベート スコープ (実行環境) が形成され、このプライベート スコープがクロージャであることがわかっています。振り返ってみると、クロージャは依然として伝説的な「高尚で習得が難しい」ものなのでしょうか?最初のインタビューの話と私が書いた 3 つの例を振り返ってみましょう。正確に言うと、これら 3 つの例はすべてクロージャの一般的な形式です。

アプリケーションシナリオ

ここで次のような要件があります。HTML ページに ul タグがあり、ul の下に 5 つの li タグがあり、任意の li とインデックス (インデックスは 0 から始まります) 位置をクリックする必要があります。クリックされた li がポップアップし、HTML 構造は次のようになります:

コードをコピーします コードは次のとおりです:


  • リスト 1

  • リスト 2

  • リスト 3

  • リスト 4

  • リスト 5



賢いので、私はすぐに次のコードを書きました:

コードをコピーします コードは次のとおりです:

var lis = document.getElementById('ul').getElementsByTagName('li');
for (var i = 0, len = lis.length; i lis[i].onclick = function () {
アラート(i);
};
}

この要件が完全に実現されているかどうかを確認するための最終テスト:

何度クリックしても、最終的にはこの結果がポップアップすることがわかりました。期待される結果は次のとおりです。リスト 1 をクリックすると 0 が表示され、リスト 2 をクリックすると 1 が表示され、リスト 3 をクリックすると表示されます。ポップアップ 2... 今この瞬間にこの写真を使いたいのですが、現在の気分を説明するには:

(デモンストレーション中にプロトタイプが設計通りに動作しなかった場合の様子)

これはなぜ常に 5 が表示されるのですか?理論的には正しいんです!それを分析するために絵を描いてみましょう:

実際、各 li に与えた onclick は、実際には関数の保存された説明文字列です。まだ信じられない場合は、この文字列の内容は上の図の赤いボックス内の内容です。真実の写真があります:

Chrome コンソールに「lis[4].onclick」と入力します。値は関数の説明です。 5 番目のリストをクリックすると、実際には lis[4].onclick() と同等になり、この関数の説明が呼び出され、このプライベート スコープ内で関数がプライベート スコープを形成することがわかります。も最初に事前解釈されてからコードが実行されます。このとき、現在のプライベート スコープには i が見つかりません。その後、ウィンドウ スコープで i が見つかるため、クリックするたびに 5 が表示されます。 。

明らかに、上記のコードはこの要件を満たしていません。このようなコードを書くのは間違っています。問題の原因を考えてみましょう。実際、その理由は、クリックするたびにウィンドウの下の i を読み取るためです。この時点で、i の値はすでに 5 なので、次のコードになります。

方法 1:

コードをコピーします コードは次のとおりです:
var lis = document.getElementById('ul').getElementsByTagName('li');
関数 fn(i) {
戻り関数 () {
アラート(i);
}
}
for (var i = 0, len = lis.length; i lis[i].onclick = fn(i);
}

方法 2:

コードをコピーします コードは次のとおりです:
var lis = document.getElementById('ul').getElementsByTagName('li');

for (var i = 0, len = lis.length; i ;(関数 (i) {
lis[i].onclick = function () {
アラート(i);
};
})(i);
}

方法 3:

コードをコピーします コードは次のとおりです:
var lis = document.getElementById('ul').getElementsByTagName('li');

for (var i = 0, len = lis.length; i lis[i].onclick = 関数 fn(i) {
return 関数 () {
アラート(i);
}
}(i);
}

私は 3 つのメソッドを一気に書きました。考え方は同じで、変数 i をプライベート変数に格納するというものです。もちろん、ここでは 2 つ目のメソッドについてのみ説明します。残りは理解できるでしょう。いつものように、段階的に分析するための絵を描きます:

コードの実行全体を詳しく説明しましたが、この関数が実行されるとき、各 li の onclick 属性は (function(i){…})(i) スコープを占有する必要があることに注意してください。外部 li によって占有されているため (この li はウィンドウ スコープの下にあります)、このスコープは破棄されません。いずれかの li をクリックすると、 function(){alert(i); } が実行され、このスコープには i がなく、 (function(){ ... })( i) スコープ i を検索し、最後に仮パラメーターで i を検索します。この仮パラメーター i の値は for ループ中に渡されます。これにより、問題は完全に解決されます。

追伸: (function(i){ … })(i) の前にセミコロンを追加する理由については、先ほど述べたように、前のステートメントでセミコロンを追加し忘れると、JavaScript エラーが発生するのを防ぐためです。解析するだけです。もちろん、上記のアプリケーション シナリオの 1 つは、タブの実装原理です。カスタム属性メソッドや DOM ノードの関係を介したインデックスの検索など、他の実装方法も可能です。このメソッドを使用する主な目的は、クロージャについての理解を深めることです。 。

概要

クロージャは、マスターするのが難しい伝説的なものではありません。関数の定義と呼び出しを理解することです。関数が呼び出されるとき、特定のスコープが外部によって占有されると、新しいプライベート スコープが形成されます。このスコープは破棄されません。 Lu Zhu はほとんど読んでいないので、間違っている場合はブロガー仲間に指摘していただきたいと思います。また、Lu Zhu の記事をサポートしてくださった皆様にも感謝いたします。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!