ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript クロージャーの簡単な紹介

JavaScript クロージャーの簡単な紹介

WBOY
WBOY転載
2023-01-21 06:30:021106ブラウズ

この記事では、JavaScript に関する関連知識を提供します。主に JavaScript クロージャに関連する問題を紹介します。クロージャの概念には多くのバージョンがあり、クロージャについては場所によって意見が異なります。以下は、見てみましょう。みんなを助けます。

JavaScript クロージャーの簡単な紹介

#クロージャとは何ですか?

クロージャの概念には多くのバージョンがあり、さまざまな場所でクロージャについてさまざまな意見があります

Wikipedia: コンピュータ サイエンスでは、クロージャ (英語: Closure)、レキシカル クロージャまたはレキシカル クロージャとも呼ばれます。関数クロージャとは、ファーストクラス関数をサポートするプログラミング言語で字句バインディングを実装するための手法です。

MDN:

closure (クロージャ) は、関数とそのバンドルされた周囲の環境状態 (字句環境字句環境 ##) です。 #) は参照の組み合わせです。 #個人的な理解:

Closure は関数 (関数を返す)

    返された関数は外部変数への参照を保存します
  • 簡単な例
function fn() {    let num = 1;    return function (n) {        return n + num
    }
}let rFn = fn()let newN = rFn(3) // 4
num 変数のスコープは fn 関数内にありますが、rFn 関数は num 変数にアクセスできます。これは、クロージャー関数が外部関数変数にアクセスできることを意味します。

ブラウザのデバッグと VSCode Nodejs のデバッグからクロージャを確認する

ブラウザ

JavaScript クロージャーの簡単な紹介VS Code は連携しますNode.js

を使用して、Closure の fn が num 変数を保存するクロージャー関数であることを確認します。 JavaScript クロージャーの簡単な紹介

古典的なクロージャ: シングルスレッドのイベント メカニズムのループ問題と解決策

for (var i = 1; i  {    console.log(i);
  }, i * 1000);
}
出力結果はすべて 6 ですが、なぜですか?

for ループは同期タスクです

    setTimeout 非同期タスク
  • 1 回ループすると、setTimeout 非同期タスクがブラウザの非同期タスクに追加されますキュー 同期タスクが完了すると、非同期タスクから新しいタスクが取得され、スレッドで実行されます。 setTimeout は外部変数 i にアクセスできるため、同期タスクが完了すると i は 6 になり、setTimeout でアクセスできる変数 i はすべて 6 になります。
解決策 1: let ステートメントを使用する

for (var i = 1; i  {    console.log(i);
  }, i * 1000);
}
解決策 2: 自己実行関数クロージャー

for (var i = 1; i  {    console.log(i);
  }, i * 1000)
  })(i)
}

解決策 3: SetTimeout で 3 番目のパラメータを渡す

3 番目のパラメータの意味: 追加パラメータ。タイマーが期限切れになると、実行される関数にパラメータとして渡されます

for (var i = 1; i  {    console.log(j);
  }, 1000 * i, i);
}

クロージャと関数curry

function add(num) {  return function (y) {    return num + y;
  };
};let incOneFn = add(1); let n = incOneFn(1);  // 2let decOneFn = add(-1); let m = decOneFn(1); // 0
add 関数の

parameters

はクロージャ関数の変数を保存します。

実際の効果

クロージャは関数型プログラミングにおいて非常に重要な役割を果たしており、JavaScript の欠点を補う Lodash やその他の初期のツール関数には、クロージャの使用シナリオが多数あります。

使用シナリオ

プライベート変数の作成

    変数のライフサイクルの延長
  • スロットル関数

スクロール動作や関数の過剰な実行を防ぐには、調整が必要です。調整関数は、パラメータとして function

time を受け入れます。これらはすべて、

function throttle(fn, time=300){    var t = null;    return function(){        if(t) return;
        t = setTimeout(() => {
            fn.call(this);
            t = null;
        }, time);
    }
}
アンチシェイク関数

setTimeoutアンチシェイク関数の単純な実装
function debounce(fn,wait){    var timer = null;    return function(){        if(timer !== null){            clearTimeout(timer);
        }
        timer = setTimeout(fn,wait);
    }
}
##は、単純なsetTimeoutバージョンです。 #React.useCallback クロージャー トラップの問題

問題の説明:

親/子

コンポーネント関係、親コンポーネントと子コンポーネントはクリック イベントを使用して状態データを同時に変更でき、子コンポーネント渡された props イベント属性を取得します。これは

useCallback

を通じて最適化されます。つまり、この最適化された関数にはクロージャ トラップがあります (初期状態値は常に保存されます) <pre class="brush:php;toolbar:false">import { useState, useCallback, memo } from &quot;react&quot;;const ChildWithMemo = memo((props: any) =&gt; {  return (    &lt;div&gt;       &lt;button&gt;Child click&lt;/button&gt;     &lt;/div&gt;   ); });const Parent = () =&gt; {  const [count, setCount] = useState(1);  const handleClickWithUseCallback = useCallback(() =&gt; {    console.log(count);   }, []); // 注意这里是不能监听 count, 因为每次变化都会重新绑定,造成造成子组件重新渲染   return (    &lt;div&gt;       &lt;div&gt;parent count : {count}&lt;/div&gt;       &lt;button&gt; setCount(count + 1)}&gt;click&lt;/button&gt;       &lt;childwithmemo&gt;&lt;/childwithmemo&gt;     &lt;/div&gt;   ); };export default Parent</pre>ChildWithMemo は最適化にメモを使用し、

handleClickWithUseCallback は最適化に useCallback を使用します
  • 問題は、サブコンポーネントをクリックしたときに出力されるカウントが初期値 (閉じた) になってしまうことです。
解決策は、 useRef を使用して操作変数関数を保存することです:

import { useState, useCallback, memo, useRef } from "react";const ChildWithMemo = memo((props: any) => {  console.log("rendered children")  return (    <div>
      <button> props.countRef.current()}>Child click</button>
    </div>
  );
});const Parent = () => {  const [count, setCount] = useState(1);  const countRef = useRef<any>(null)

  countRef.current = () => {    console.log(count);
  }  return (    <div>
      <div>parent count : {count}</div>
      <button> setCount(count + 1)}>click</button>
      <childwithmemo></childwithmemo>
    </div>
  );
};export default Parent</any>
この問題に対応して、React は useEvent を追加するというコミュニティの提案を一度承認しましたが、その後 useEvent セマンティクスがレンダリングの最適化のために、React はコンパイル最適化ソリューションを採用しています。実はuseEffectでも同様の問題が発生しますので、使用する際はクロージャトラップに注意してください。

パフォーマンスの問題

  • クロージャを勝手に定義しないでください。定義したら、破棄に適した場所を見つける必要があります。クロージャの変数はメモリに保存され、破棄されないため、大量のメモリを占有します。

クロム パネル機能のタイムライン プロファイル パネルを使用します

  1. 開発者ツールを開き、タイムライン パネルを選択します
  2. Capture#フィールドの一番上の ## メモリ
  3. をチェックし、左上隅の記録ボタンをクリックします。
  4. ページ上でさまざまな操作を実行して、ユーザーの使用状況をシミュレートします。
  5. 一定の時間が経過した後、ダイアログ ボックスの停止ボタンをクリックすると、この期間のメモリ使用量がパネルに表示されます。
[関連する推奨事項:

JavaScript ビデオ チュートリアル Web フロントエンド ]

以上がJavaScript クロージャーの簡単な紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.imで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。