js における関数のネストとクロージャの詳細

js における関数のネストとクロージャの詳細

序文:

今日は、クロージャについての私の理解についてお話しします。この問題について議論する前に、まず変数のドメインを理解しましょう。
js では、変数定義スコープにはグローバル スコープとローカル スコープが含まれます。 es6の新しい変数宣言キーワードは、いくつかの変数スコープの混乱を解決するために導入されました。グローバル スコープについてはここでは説明しません。主に関数の範囲について説明します。

1. 範囲

簡単に言えば、関数のスコープとは、関数の中括弧内のスペースのことです。まず、この概念をよりよく理解するのに役立つ 2 つの例を見てみましょう。

関数f1(){
  n = 999とする
  コンソール.log(n)
}
f1() // 999

関数f2(){
  n = 999とする
}
alert(n); // エラーメッセージ

2. 関数の戻り値

クロージャについて話す前に、関数の戻り値について話す必要があります。関数の戻り値に関しては、編集者は今年の初めに初めて理解が深まりました。戻り値のない関数は実行後にundefined返し、戻り値のある関数は実行後に該当する戻り値になります。こんな感じ

// 戻り値のない関数 function f1(){
  警告(666)
}
console.log(f1()) // ポップアップウィンドウが表示されたら、コンソールに undefined を出力します

// 戻り値関数 f2(){
  警告(666)
  'over' を返す
}
console.log(f2()) // ポップアップウィンドウが表示されたら、コンソールに出力します。もちろん、文字列、Bealon、または関数を返すこともできます。

3. 関数のネスト

「リファクタリング - 既存コードの設計を改善する」では、js 構文では関数内に関数をネストできると提案されていますが、すべてのプログラミング言語でこれができるわけではありません。いわゆるコードのネストとは、関数内に関数宣言があることを意味します。

このような:

関数outer(){
  名前を 'lilei' にします
  関数内部(){
    console.log(名前)
  }
}  

4. 終了

js におけるローカル変数スコープの問題については、以前に説明しました。実際のプロジェクトでは、関数の外部から関数内の変数にアクセスする必要があります。このとき、ローカル変数スコープの問題に対処する必要があります。不可能に思えますが、クロージャの出現によりこの問題は解決されます。

関数outer(){
  名前を 'lilei' にします
  関数内部(){
    戻り名
  }
  内部を返す
}

上記は典型的なクロージャ関数です。このクロージャ関数を使用すると、次のことができます。

g = アウター() とする
console.log(g()) // リレイ


これまでのところ、グローバル関数内でのローカル変数へのアクセスは解決されています。しかし、帰り道で、この機能を実現するために、このトラブルを回避することはできないだろうか? そういった機能を通じてニーズに応えることもできるのではないか? と考えていました。

関数outer(){
  名前を 'lilei' にします
  戻り名
}

console.log(outer()) // リレイ  


実際、上記のコードはクロージャを通じてコン​​ソールに出力されるものと同じなので、なぜクロージャを導入するのでしょうか?理解するのに 1 週​​間近くかかりました。変数 -> 関数 -> クラスのようなものです。レベルが上がるごとに、徐々に改善していきます。関数を通じて、変数だけでは実現できないデータ処理などのロジックをさらに実装できます。

5. クロージャの実用的応用

上記でエディターはクロージャを紹介しましたが、実際のプロジェクトではクロージャはどのように応用されるのでしょうか?まず次のコードを見てみましょう。

1. 内部変数名と関数実行の一時停止を非表示にする

関数outer() {
    名前を 1 にする
    関数内部() {
        名前を返す++
    }
    内部を返す
}
g = アウター() とする
コンソールログ(g()) // 2
コンソール.log(g()) // 3
コンソールログ(g()) // 4
コンソールログ(g()) // 5

2. setTimeout関数はパラメータを渡す

デフォルトの setTimeout は次のとおりです。

私も以前にこれを試したことがあります。

関数 f1(p) {
    コンソールログ(p)
}
setTimeout(f1(666),3000) // 遅延なし、666を直接出力

遅延を介して関数にパラメータを渡す場合、クロージャの役割が明らかになります。

関数 f1(a) {
    関数f2() {
        コンソールにログ出力します。
    }
    f2 を返します。
}
var fun = f1(1);
setTimeout(fun,1000); // 1秒後に1を出力します

3. コールバック

動作を定義し、それをユーザー イベント (クリックまたはキー押下) に関連付けます。コードは通常、コールバック (イベントがトリガーされたときに呼び出される関数) としてイベントにバインドされます。次のコードのように

<!DOCTYPE html>
<html lang="ja">
<ヘッド>
    <メタ文字セット="UTF-8">
    <title>テスト</title>
</head>
<本文>
    <a href="#" rel="外部 nofollow" rel="外部 nofollow" rel="外部 nofollow" id="size-12">12</a>
    <a href="#" rel="外部 nofollow" rel="外部 nofollow" rel="外部 nofollow" id="size-20">20</a>
    <a href="#" rel="外部 nofollow" rel="外部 nofollow" rel="外部 nofollow" id="size-30">30</a>

    <script type="text/javascript">
        関数 changeSize(サイズ){
            関数()を返す{
                document.body.style.fontSize = サイズ + 'px';
            };
        }

        var size12 = changeSize(12);
        var size14 = changeSize(20);
        var size16 = changeSize(30);

        document.getElementById('size-12').onclick = size12;
        document.getElementById('size-20').onclick = size14;
        document.getElementById('size-30').onclick = size16;
</スクリプト>
</本文>
</html>

4. 手ぶれ補正機能

コールバックはイベントがトリガーされてから n 秒後に実行されます。n 秒以内に再度トリガーされた場合は、タイミングが再開されます。

実装の鍵はsetTimeout関数にあります。タイミングを保存するための変数が必要なので、グローバルな純粋性を維持するために、クロージャを使用して実装できます。このような:

/*
* fn [関数] 手ぶれ補正を必要とする関数* delay [数値] ミリ秒、手ぶれ補正の期限値*/
関数デバウンス(fn,delay){
    let timer = null //クロージャ付き return function() {
        if(タイマー){
            clearTimeout(timer) //この分岐ステートメントを入力すると、タイミング プロセスが現在進行中であり、同じイベントが再度トリガーされることを示します。したがって、現在のタイミングをキャンセルしてタイミングを再開するには、timer = setTimeOut(fn,delay) を使用します。 
        }それ以外{
            timer = setTimeOut(fn,delay) // このブランチに入るということは、現在タイミングがないことを意味し、タイミングを開始します}
    }
}

6. クロージャ内の内部変数を隠すのと同様に、クラスを使用して関数を実装する

上記はクロージャの実際の応用です。夜眠れないときに、同じニーズを考えました。クラスで実装できるでしょうか?最終的に、多くの苦労の末、答えは「はい」です。

クラス Adder{
    コンストラクタ(c){
        this._c = c
    }
    増加(){
        これ._c++ 
    }
    減少(){
        this._c --
    }
    finalNum() を取得する{
        これを返します。_c
    }
}
c = new Adder(1) とする
c.増加()
コンソール.log(c.finalNum) // 2
c.増加()
コンソール.log(c.finalNum) // 3
c.増加()
コンソール.log(c.finalNum) // 4
c.減少()
コンソール.log(c.finalNum) // 3

参考記事:

https://www.cnblogs.com/gg-qq...

https://www.cnblogs.com/pikac...

https://developer.mozilla.org...

以下もご興味があるかもしれません:
  • JavaScript における関数のネスト
  • 1つの記事でJavaScriptのクロージャ関数について学ぶ
  • JavaScript クロージャの説明
  • Javascript のスコープとクロージャの詳細
  • JS の難しさ 同期と非同期、スコープとクロージャ、プロトタイプとプロトタイプ チェーンの詳細な説明
  • JavaScript のクロージャによって発生する問題を回避する
  • JavaScript のクロージャの問題の詳細な説明
  • JavaScript 関数の使用方法の詳細な説明 [関数の定義、パラメータ、バインディング、スコープ、クロージャなど]

<<:  CSS で水平方向と垂直方向に中央揃えする 10 の方法を教えます (要約)

>>:  ナビゲーションデザインと情報アーキテクチャ

推薦する

数字当てゲームを実装するための純粋なJavaScript

100 以内の自然数をランダムに選択し、プレイヤーに 10 ラウンド以内にその数を推測させる数字推...

Nginx Rewriteモジュールを使用するいくつかのシナリオ

アプリケーションシナリオ1: ドメイン名ベースのリダイレクト会社の古いドメイン名は www.accp...

入力タイプの制限(複数の方法)

1. 入力・貼り付けできるのは中国語のみ<input onkeyup="value=...

JS でパブリッシュ サブスクライブ モデルを作成する

目次1. シーン紹介2 コードの最適化2.1 ファンを増やす問題を解決する2.2 作品追加の問題を解...

基礎知識: ウェブサイトのアドレスの前の http はどういう意味ですか?

HTTPとは何ですか?ウェブサイトを閲覧したいときは、ブラウザのアドレス バーにウェブサイトのアド...

MySQL ルートパスワードを変更する 4 つの方法 (要約)

方法1: SET PASSWORDコマンドを使用するまずMySQLにログインします。フォーマット: ...

Dockerは終了状態で起動します

docker run後、ステータスは常にExitedになります解決:パラメータを追加: -it do...

CSS 背景と境界タグの例の詳細な説明

1. CSS背景タグ1.背景色を設定するbackground-ground-color プロパティは...

Macシステムをインストールした後にVMWareがフルスクリーンで表示できない問題を解決する

システム: VMTOOLs ダウンロード:リンク: https://pan.baidu.com/s/...

MySQL 5.6.22 のインストールと設定方法のグラフィックチュートリアル

このチュートリアルでは、MySQL5.6.22のインストールと設定方法の具体的なコードを参考までに共...

webpack-dev-server のコア概念とケースの詳細な説明

webpack-dev-server コアコンセプトWebpack の ContentBase と ...

nginx を介してローカルでリバースプロキシを構成するプロセス全体

序文Nginx は、イベント駆動型の非同期非ブロッキング処理フレームワークを使用する軽量 HTTP ...

中国語フォントの英語名まとめ

CSS の font-family プロパティを使用して中国語フォントを参照する場合、フォントを定義...

CSS を使用して 3 列のアダプティブ レイアウト (両側は固定幅、中央はアダプティブ) を実現します。

いわゆる 3 列適応レイアウトとは、両側の幅が固定され、中央のブロックの幅が適応されることを意味しま...

Ubuntu にグラフィック ドライバーが正常にインストールされたかどうかを確認する方法

次のコマンドを実行します: glxinfo | grep レンダリング結果が「はい」の場合、グラフィ...