JavaScriptクロージャの原理と機能の詳細な説明

JavaScriptクロージャの原理と機能の詳細な説明

導入

例示する

この記事では、JavaScript クロージャの役割、目的、原則について説明します。

閉鎖の定義

クロージャとは、内部関数が外部関数内でも常に外部関数で宣言された変数とパラメータにアクセスできることを意味します。

番号が返却された後(有効期限が切れます)。

クロージャの役割(特徴)

1. 関数内の関数

2. 内部関数は外部関数のパラメータや変数を参照できる

3. 外部関数のパラメータと変数は内部関数によって参照されるため、ガベージコレクションの対象になりません。

クロージャとグローバル変数

クロージャの使用

カレー作り

パラメータを通じてさまざまな関数を生成できます。

関数makeWelcome(x) {
	関数(y)を返す{
		x + y を返します。
	};
}
 
sayHello = makeWelcome("Hello,") とします。
sayHi = makeWelcome("Hi,") とします。
 
console.log(sayHello("Tony"));
console.log(sayHi("Tony"));

結果

こんにちは、トニー

こんにちは、トニー

パブリック変数の実装

要件: 呼び出されるたびに 1 回増加するアキュムレータを実装します。

関数makeCounter(){
	count = 0 とします。
	関数 innerFunction(){
		count++ を返します。
	}
	内部関数を返します。
}
カウンターを makeCounter() にします。
 
コンソールにログ出力します。
コンソールにログ出力します。
コンソールにログ出力します。

結果

0

1

2

キャッシュ

処理に非常に時間のかかる関数オブジェクトがあるとします。計算された値は保存できます。この関数が呼び出されると、まずキャッシュ内で検索されます。見つからない場合は計算が行われ、キャッシュが更新されて値が返されます。見つかった場合は、見つかった値が直接返されます。

クロージャは外部参照を解放しないため、関数内の値を保持できるため、これを実行できます。

簡潔にするために、この記事では読み取り/書き込みキャッシュの例を直接記述します。 (読み取れない場合は計算してキャッシュに保存する代わりに)。

キャッシュ = 関数 () {
	// マップでは任意のタイプのキーを使用できます。 let storage = {} と記述した場合、キーには文字列のみを指定できます。let storage = new Map();
	戻る {
		setCache: 関数(k, v) {
			ストレージ[k] = v;
		},
		getCache: 関数 (k) {
			ストレージ[k]を返します。
		},
		キャッシュを削除する: 関数 (k) {
			ストレージ[k]を削除します。
		}
	}
}();
 
キャッシュをsetCache('a', 1);
コンソールログ(キャッシュの取得キャッシュ('a'))

結果

1

カプセル化(属性のプライベート化)

内部変数には、提供されたクロージャを通じてのみアクセスできます。 (この方法は良くないので、プロトタイプチェーンを使用することをお勧めします)。

person = function(){ とする
	// 変数のスコープは関数内にあり、外部からアクセスすることはできません。let name = "defaultName";
 
	戻る {
		getName: 関数(){
			名前を返します。
		},
		setName: 関数(newName){
			名前 = 新しい名前;
		}
	}
}();
 
console.log(人名);
コンソールにログ出力します。
person.setName("こんにちは");
コンソールにログ出力します。

結果

未定義

デフォルト名

こんにちは

閉鎖の原則

カウンターを例に挙げてみましょう:

関数makeCounter() {
	count = 0 とします。
	関数()を返す{
		count++ を返します。
	};
}
カウンターを makeCounter() にします。
コンソールにログ出力します。
コンソールにログ出力します。
コンソールにログ出力します。

結果

0

1

2

各 makeCounter() 呼び出しの開始時に、その makeCounter ランタイムの変数を格納するための新しい LexicalEnvironment オブジェクトが作成されます。

したがって、ネストされた語彙環境には 2 つのレベルがあります。

makeCounter() を実行すると、1 行だけを占めるネストされた関数 return count++ が作成されます。まだ実行していません。作成しただけです。

すべての関数は、それが「誕生」したときに作成された語彙環境を記憶します。理由: すべての関数には、関数が作成された語彙環境への参照を保持する [[Environment]] と呼ばれる隠し属性があります。

したがって、counter.[[Environment]] には {count: 0} 字句環境への参照があります。これは、関数が呼び出される場所に関係なく、関数が作成された場所を記憶する方法です。 [[Environment]] 参照は関数の作成時に設定され、永続的に保存されます。

その後、counter() が呼び出されると、その呼び出しに対して新しい Lexical Environment が作成され、その外部 Lexical Environment 参照が counter.[[Environment]] から取得されます

ここで、counter() 内のコードが count 変数を探すとき、最初に自身の Lexical Environment (ローカル変数がないため空) を検索し、次に外側の makeCounter() の Lexical Environment を検索して、見つかった場所に count 変数を固定します。

変更します (変数が存在する語彙環境内の変数を更新します)。

実行後のステータスは次のとおりです。

counter() を複数回呼び出すと、count 変数は同じ位置で 2、3 などに増加します。

ガベージコレクション

導入

通常、関数呼び出しが完了すると、レキシカル環境とその中のすべての変数は、参照がなくなるためメモリから削除されます。

JavaScript の他のオブジェクトと同様に、語彙環境はアクセス可能である限りメモリ内に保持されます。ただし、関数が終了した後もまだ到達可能なネストされた関数がある場合、その関数には、Lexical Environment を参照する [[Environment]] 属性があります。

関数が完了した後も、レキシカル環境がまだ到達可能である場合、ネストされた関数は有効なままになります。例えば:

関数f(){
    値を 123 とします。
    関数()を返す{
        アラート(値);
    }
}
// g.[[Environment]] は対応する f() 呼び出しの語彙環境への参照を格納します。let g = f();

f() が複数回呼び出され、返された関数が保存されると、対応するすべての LexicalEnvironment オブジェクトもメモリ内に保持されます。例えば:

関数f(){
    値を Math.random() とします。
    関数を返す(){
        アラート(値);
    };
}
 
// 配列内の 3 つの関数。それぞれが対応する f() の語彙環境に関連付けられています。let arr = [f(), f(), f()];

LexicalEnvironment オブジェクトが到達不能になると、他のオブジェクトと同様に終了します。つまり、少なくとも 1 つのネストされた関数がそれを参照している限り存在します。

次のコードでは、ネストされた関数が削除されると、それを囲むレキシカル環境 (およびその中の値) もメモリから削除されます。

関数f(){
    値を 123 とします。
    関数()を返す{
        アラート(値);
    }
} 
let g = f(); // g関数が存在する間、値はメモリに保持されます g = null; // これでメモリがクリーンアップされます

実際の開発における最適化

これまで見てきたように、理論上は関数が到達可能である場合、その外側にあるすべての変数も存在することになります。しかし実際には、JavaScript エンジンはそれを最適化しようとします。変数の使用状況を分析し、未使用の外部変数があることがコードから明らかな場合は、それらが削除されます。

V8 (Chrome、Opera) での重要な副作用は、そのような変数がデバッグで使用できなくなることです。

Chrome ブラウザの開発者ツールを開き、次のコードを実行してみてください。

    関数f(){
        値を Math.random() とします。
        関数g(){
            デバッガ;
        }
        g を返します。
    } 
    g = f() とします。
    g();

コードが「debugger;」の場所まで実行されると一時停止します。このとき、コンソールに console.log(value); と入力します。

結果: エラー: VM146:1 キャッチされない参照エラー: 値が定義されていません

これにより、興味深いデバッグの問題が発生する可能性があります。たとえば、予想される変数の代わりに、同じ名前の外部変数が表示されることがあります。

let value = "サプライズ!";
関数f(){
    value = "最も近い値";
    関数g(){
        デバッガ;
    }
    g を返します。
}
g = f() とします。
g();

コードが「debugger;」の場所まで実行されると一時停止します。このとき、コンソールに console.log(value); と入力します。

結果: 出力: 驚き。

以上がJavaScriptクロージャの原理と機能の詳細な内容です。JavaScriptクロージャの詳細については、123WORDPRESS.COMの他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • JSにおけるクロージャの役割について詳しく話しましょう
  • JavaScript の高度なクロージャの説明
  • JavaScript クロージャの詳細
  • JavaScript クロージャの説明
  • JavaScriptのクロージャとは何かを学びましょう

<<:  tomcat ログ ディレクトリ内のログ ファイルの分析 (概要)

>>:  MySQLの整数データ型tinyintの詳細な説明

推薦する

VMware仮想マシンの起動時に黒い画面が表示される問題を解決する

# VMware ハードディスクの起動優先順位を調整するステップ 1: 電源をオンにすると、BIOS...

Centos7.4 サーバーへの Apache のインストールとインストール プロセス中に発生した問題の解決策

この記事では、CentOS 7.4 サーバーに Apache をインストールする方法と、インストール...

JS ES の新機能、変数分離割り当て

目次1. 配列の分離割り当て1.1 配列分離割り当てとは何ですか? 1.2 配列分離割り当てに失敗し...

Apache Superset を使用して ClickHouse データを視覚化する 2 つの方法

Apache Superset は、データを表示および探索する方法を提供する強力な BI ツールで...

クラウド サーバー Ubuntu_Server_16.04.1 に MySQL をインストールしてリモート接続を有効にする方法

1. MySQLをインストールします。対応するソフトウェアをインストールするには、次の 3 つのコマ...

Faint: 「Web2.0 を使用して標準に準拠したページを作成する」

今日、ある人がウェブサイト開発プロジェクトについて話をしてくれました。具体的な要件について話すと、「...

ウェブデザインにおけるグリッドシステム

グリッドシステムの形成1692年、新しく即位したフランス国王ルイ14世は、フランスの印刷技術のレベル...

Windows で削除された MySQL 8.0.17 のルート アカウントとパスワードを回復する方法

少し前にSQLの独学を終え、MySQL 8.0.17をダウンロードしました。インストールして設定した...

Nginx の起動に失敗した場合のいくつかのエラー処理の詳細な説明

Nginx を Web サーバーとして使用する際に、次の問題が発生しました。 1. nginxの起動...

nginx+FastDFS を使ってファイル管理システムを段階的に構築する

目次1. FastDFS の概要1. はじめに2. FastDFSストレージ戦略3. FastDFS...

カルーセル効果を実現するjQueryプラグイン

毎日jQueryプラグイン - カルーセルチャートを実装するためのjQueryプラグイン。参考までに...

Linux システムでの gcc コマンドの使用法の詳細な説明

目次1. 前処理2. コンパイル3. コンパイル4. リンク1. gccのインストール(Ubuntu...

Docker コンテナ データ ボリュームの名前付きマウントと匿名マウントの問題

目次コンテナデータボリュームとはコンテナ データ ボリュームが必要なのはなぜですか?使用データボリュ...

Linux 環境の Apache で https サービスを有効にする方法の詳細な説明

この記事では、Linux 環境の Apache で https サービスを有効にする方法について説明...

MySQL 実験: explain を使用してインデックスの傾向を分析する

概要インデックス作成は、MySQL で習得しなければならないスキルであり、MySQL クエリの効率を...