序文フロントエンドの学習や開発では、クロージャやガベージコレクションの仕組みが難しいことが多く、面接でもそのような問題に遭遇することがよくあります。この記事では、勉強中や仕事中にこの側面についてメモした内容を記録します。 文章1. 終了クロージャは、Javascript 言語の難しさであると同時に、その特徴でもあります。多くの高度なアプリケーションはクロージャに依存しています。 JavaScript 開発者にとって、クロージャを理解することは非常に重要です。 1.1 クロージャとは何ですか?クロージャとは、別の関数の変数を参照する関数です。内部関数が外部に返されて保存されるときに生成されます。(内部関数のスコープチェーンAOは、外部関数のAOを使用します) 変数は参照されるためリサイクルされず、プライベート変数をカプセル化するために使用できますが、不必要なクロージャによってメモリ消費が増加するだけです。 1.2 クロージャの特性①関数ネスト機能 ②関数は関数外のパラメータや変数を参照できる ③パラメータと変数はガベージコレクションメカニズムによってリサイクルされない 1.3 クロージャを理解する私たちがよく知っているスコープ チェーンの知識に基づいて、カウンターの問題、つまり関数が呼び出されるたびにカウンターを 1 つ増やす関数を実装する方法を見てみましょう。 var カウンタ = 0; 関数demo3(){ コンソールログ(カウンタ+=1); } デモ3();//1 デモ3();//2 var カウンタ = 5; デモ3(); //6 上記の方法では、カウンタの値がどこかで変更されると、カウンタは無効になります。JavaScript では、関数を関数内に埋め込むクロージャを使用してこの問題を解決します。クロージャを使用してこれを実装する方法を見てみましょう。 関数add(){ var カウンタ = 0; 関数 plus() を返す { カウンター += 1; 返品カウンター } } var count = 追加() コンソール.log(count()) // 1 var カウンタ = 100 コンソール.log(count()) // 2 上記はクロージャの使用例です。add 関数には plus 関数が埋め込まれており、count 変数は返された関数を参照します。外部関数 add が実行されるたびに、メモリ空間の一部が開かれます。外部関数のアドレスは異なり、新しいアドレスが再作成されます。plus 関数は add 関数内にネストされているため、ローカル変数 counter が生成されます。count 関数が呼び出されるたびに、ローカル変数の値が 1 ずつ増加し、実際のカウンター問題が実現されます。 1.4 クロージャの主な実装形式ここでの学習閉鎖には主に 2 つの形式があります。 ① 上記の例で使用した戻り値としての関数。 関数 showName(){ var name="xiaomi" 関数()を返す{ 戻り名 } } var name1=showName() コンソールログ(name1()) クロージャは、別の関数内から変数を読み取ることができる関数です。クロージャは、関数の内部と関数の外部を接続するブリッジです。 ②パラメータとして渡されるクロージャ 変数番号 = 15 var foo = 関数(x){ if(x>num){ コンソール.log(x) } } 関数foo2(fnc){ 変数番号=30 フンク(25) } foo2(foo) // 25 上記コードでは、関数 foo が関数 foo2 にパラメータとして渡されています。foo2 が実行されると、foo に 25 がパラメータとして渡されます。このとき、x>num の判定における num の値は、foo2 内の num ではなく、作成した関数のスコープ内の num、つまりグローバル num であるため、25 が出力されます。 1.5 クロージャの利点と欠点アドバンテージ: ①関数内の変数のセキュリティを保護し、カプセル化を実装し、変数が他の環境に流れて名前の競合が発生するのを防ぎます。 ② キャッシュ用に変数をメモリ上に保持する(ただし、過度の使用はメモリを消費するためデメリットもある) ③匿名の自己実行関数はメモリ消費量を削減できる 欠点: ① 上記の点の 1 つは、参照されたプライベート変数を破棄できないため、メモリ消費量が増加し、メモリ リークが発生することです。解決策は、変数を使用した後に手動で null を割り当てることです。 ② 次に、クロージャはクロスドメインアクセスを伴うため、パフォーマンスの低下を引き起こします。クロススコープ変数をローカル変数に格納し、ローカル変数に直接アクセスすることで、実行速度への影響を軽減できます。 1.6 クロージャの使用(var i = 0; i < 5; i++) の場合 { setTimeout(関数() { コンソールログ( i); }, 1000); } コンソールにログ出力します。 上の質問を見てみましょう。これは非常に一般的な質問ですが、出力はどうなるでしょうか? ほとんどの人は、出力結果が 5、5、5、5、5、5 であることを知っています。注意深く観察すると、この問題には多くの巧妙な側面があることに気付くかもしれません。これらの 6 つの 5 の具体的な出力順序は何ですか? 5 -> 5,5,5,5,5,5。同期と非同期を理解している人なら、この状況は簡単に理解できるでしょう。上記の質問に基づいて、5 -> 0,1,2,3,4 の連続出力を実現する方法を考えてみましょう。 (var i = 0; i < 5; i++) の場合 { (function(j) { // j = i setTimeout(関数() { コンソールログ(j); }, 1000); })(私); } コンソールログ( i); //5 -> 0,1,2,3,4 このように、for ループに匿名関数が追加されます。匿名関数の入力パラメータは、毎回の i の値です。同期関数が 5 を出力してから 1 秒後、01234 を出力し続けます。 (var i = 0; i < 5; i++) の場合 { setTimeout(関数(j) { コンソールにログ出力します。 }, 1000, i); } コンソールログ( i); //5 -> 0,1,2,3,4 setTimeout API をよく見ると、3 番目のパラメーターがあることがわかります。これにより、匿名関数を介して i を渡すという問題が回避されます。 var 出力 = 関数 (i) { setTimeout(関数() { コンソールにログ出力します。 }, 1000); }; (var i = 0; i < 5; i++) の場合 { output(i); // ここで渡されたiの値がコピーされます} コンソールにログ出力します。 //5 -> 0,1,2,3,4 ここでは、クロージャを使用して関数式を for ループのパラメータとして渡し、上記の効果も実現します。 (i = 0; i < 5; i++ とします) { setTimeout(関数() { console.log(新しい日付、i); }, 1000); } console.log(新しい日付、i); //5 -> 0,1,2,3,4 let ブロックのスコープを知っている人は上記の方法を思いつくでしょう。しかし、0 -> 1 -> 2 -> 3 -> 4 -> 5 のような効果を実現したい場合はどうすればよいでしょうか? (var i = 0; i < 5; i++) の場合 { (関数(j) { setTimeout(関数() { console.log(新しい日付、j); }, 1000 * j); // ここで0〜4のタイマー時間を変更します })(i); } setTimeout(function() { // ここでタイマーを追加し、タイムアウトを 5 秒に設定します console.log(new Date, i); }, 1000 * i); //0 -> 1 -> 2 -> 3 -> 4 -> 5 Promise を通じて実装された次のコードもあります。 定数タスク = []; for (var i = 0; i < 5; i++) { // ここでの i の宣言は let に変更できません。変更したい場合はどうすればよいでしょうか? ((j) => { タスクをプッシュ(新しいPromise((解決) => { タイムアウトを設定する(() => { console.log(新しい日付、j); 解決(); // ここで解決する必要があります。そうしないと、コードは期待どおりに動作しません。 }, 1000 * j); // タイマーのタイムアウトが徐々に増加します })); })(私); } Promise.all(tasks).then(() => { タイムアウトを設定する(() => { console.log(新しい日付、i); }, 1000); // タイムアウトを 1 秒に設定するだけでよいことに注意してください }); //0 -> 1 -> 2 -> 3 -> 4 -> 5 const task = []; // 非同期操作のPromiseはこれです const 出力 = (i) => 新しい Promise((resolve) => { タイムアウトを設定する(() => { console.log(新しい日付、i); 解決する(); }, 1000 * i); }); // すべての非同期操作を生成する for (var i = 0; i < 5; i++) { タスクをプッシュします(出力(i)); } //非同期操作が完了したら、最後のiを出力します Promise.all(tasks).then(() => { タイムアウトを設定する(() => { console.log(新しい日付、i); }, 1000); }); //0 -> 1 -> 2 -> 3 -> 4 -> 5 // 他の言語でスリープをシミュレートします。実際には任意の非同期操作が可能です。const sleep = (timeoutMS) => new Promise((resolve) => { タイムアウトを設定します(解決、タイムアウトMS); }); (async () => { // 宣言時に実行される非同期関数式 for (var i = 0; i < 5; i++) { もし (i > 0) { スリープ(1000)を待機します。 } console.log(新しい日付、i); } スリープ(1000)を待機します。 console.log(新しい日付、i); })(); //0 -> 1 -> 2 -> 3 -> 4 -> 5 上記のコードではクロージャが使用されています。簡単に言うと、クロージャは親関数内の同じアドレスにある対応する変数の最終値を見つけます。 2. ガベージコレクションの仕組みJavaScript のメモリ管理は自動的かつ目に見えない形で行われます。プリミティブ型、オブジェクト、関数などを作成しますが、これらすべてにメモリが必要です。 一般的に使用されるガベージ コレクションの方法には、マーク スイープと参照カウントの 2 つがあります。 1. マークアンドスイープ ガベージ コレクターが実行されると、メモリに格納されているすべての変数がマークされます。次に、環境内の変数と、環境内の変数によって参照されるトークンを削除します。 これ以降にマークされた変数は、環境内の変数からアクセスできなくなるため、削除されたと見なされます。 やっと。ガベージ コレクターはメモリのクリーンアップを完了し、マークされた値を破棄し、それらが占有していたメモリ領域を再利用します。 2. 参照カウント 参照カウントとは、各値が参照される回数を追跡することを意味します。変数が宣言され、その変数に参照型が割り当てられると、値の参照カウントは 1 になります。 逆に、この値への参照を含む変数が別の値を取得すると、この値への参照数は 1 減少します。引用数が0になると、 つまり、この値にアクセスする方法はもうないので、この値が占有しているメモリ領域を再利用することができます。こうすることで、次にガベージコレクターが実行されるときに、 参照カウントが 0 の値によって占有されているメモリを解放します。 要約するjs クロージャとガベージ コレクションのメカニズムに関するこの記事はこれで終わりです。より関連性の高い js クロージャとガベージ コレクションのコンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM を応援していただければ幸いです。 以下もご興味があるかもしれません:
|
<<: mysqlのkey_lenの計算方法についての簡単な説明
>>: Nginx 仮想ホスト (IP ベース) を構成する 3 つの方法の詳細な説明
1. ホットリンクの原則1.1 Webページの準備Web ソース ホスト (192.168.153...
目次MySQL ログファイルバイナリログBinlogログがオンになっていますログ記録を有効にする方法...
問題の説明プラグインをインストールした後、ES7 React/Redux/GraphQL/React...
CSS 要素内の計算されたスタイル (つまり、カスケード後の最終的なスタイル) を取得するには、W3...
序文この記事では、SQL インジェクションを回避するために pdo の前処理メソッドを使用します。詳...
CSS3 の 3D 効果を使用して立方体を作成する方法を学ぶと、3D シーンの回転と変位のプロパティ...
目次1. オペレーター1.1 算術演算子1.2 インクリメント演算子とデクリメント演算子1.3 比較...
目次1. JavaScriptはHTMLでキャンバスを使用する2. ページストレージ技術1. Jav...
Apache Log4j2 が核レベルの脆弱性を報告し、スタックリーダーの友人たちは大騒ぎになりまし...
この記事では、ドラッグ可能なモーダルボックスを実装するためのJavaScriptの具体的なコードを参...
写真を背景にしてリンクを記載します。たとえば、ウェブサイトのロゴ画像などです。例: ポテトのロゴ画像...
目次ネット上の質問から生まれた思考MySQL ソースコマンドネット上の質問から生まれた思考今日仕事中...
この記事では、円形カルーセルを実装するためのJavaScriptの具体的なコードを参考までに紹介しま...
この記事では、explain を使用して SQL ステートメントを分析する方法を紹介します。実際、イ...
いくつかの理由(好奇心も含む)から、数週間前に Linux デスクトップとして Xfce を使い始め...