Javascript のスコープとクロージャの詳細

Javascript のスコープとクロージャの詳細

1. 範囲

簡単に言えば、スコープとは、変数が定義されているプログラム内の領域を指し、現在実行されているコードの変数へのアクセス権を決定します。

ES5 では、一般的にスコープ タイプは次の 2 つだけです。

  • グローバルスコープ:グローバルスコープはプログラムの最も外側のスコープであり、常に存在します。
  • 関数スコープ:関数スコープは、関数が定義され、親関数スコープまたはグローバル スコープに含まれている場合にのみ作成されます。

概念について説明した後、次のコードを見てみましょう。

変数a = 100
関数テスト(){
    var b = a * 2
    変数a = 200
    変数 c = a/2
    コンソールログ(b)
    コンソール.log(c)
}
test() // ここで何が印刷されるでしょうか?

分析:

  • まず、このコードはグローバルスコープと関数スコープを形成します
  • グローバルスコープに100の値を持つ変数aがある
  • ローカル変数 b、a、c はテスト関数スコープ内で定義されます。
  • ここでは変数の昇格が行われています。変数の昇格はまず関数スコープ内で実行されます: var b; var a; var c;
  • 次に、b に値を割り当てます。この時点では、a には値が割り当てられていないため、a の値は未定義です。次に、a*2 が加算されるため、b は NaN になります。
  • 次に、a に値 200 を割り当て、c に値 a/2 (つまり 100) を割り当てます。

つまりNaN、100が出力される。

ES6では、新しいブロックスコープが追加されました

簡単に言えば、中括弧{...}内の領域がブロックレベル スコープですが、 Javascriptブロックレベル スコープをネイティブにサポートしていません。ブロックレベル スコープを作成するには、 ES6で提案されているletconstを使用する必要があります。

// ES5
真の場合{
  var name = '南九'
}
console.log(name) // ナンジュウ // ES6
真の場合{
  年齢を18とする
}
console.log(age) // エラーが報告されます

2. スコープチェーン

実行可能コード内の変数にアクセスする場合、まずその変数が現在のスコープ内に存在するかどうかを確認します。存在する場合は、すぐに戻ります。存在しない場合は、親スコープ内を検索し、グローバル スコープが見つかるまで検索を続けます。このスコープ ネスト メカニズムを作用域鏈

3. 語彙の範囲

詞法作用域は、スコープの実用的なモデルです。レキシカル スコープは、 JavaScriptで使用されるスコープの一種です。レキシカル スコープは、靜態作用域とも呼ばれます。

いわゆるレキシカルスコープは、コードを書くときに変数とスコープをどこに記述するかによって決まります。つまり、レキシカルスコープは静的スコープであり、コードを書くときに決定されます。関数のスコープは、実際に呼び出される場所ではなく、宣言される場所によって決まります。

MDN ではクロージャを次のように定義しています。

関数は、その周囲 (語彙環境) への参照とバンドルされます (または、関数は参照に囲まれています)。このような組み合わせは、 closureと呼ばれます。

つまり、クロージャを使用すると、内部関数内から外部関数のスコープにアクセスできるようになります。 JavaScriptでは、関数が作成されるたびに、関数の作成と同時にクロージャも作成されます。

次のように結論付けることができます。

閉包= 函數+ 外層作用域

まずはコードを見てみましょう:

var name = 'フロントエンドNanjiu'

関数 say() {
  console.log(名前)
}
言う()

分析: say関数は外側のスコープ内の変数 a にアクセスできるので、これはクロージャを形成しませんか?

「Javascript Definitive Guide」という本に次のような一文があります。厳密に言えば、すべてのJavaScript関数はクロージャです。

しかし、これは単なる理論的な閉鎖であり、私たちが通常使用するものとは異なります。上記の例は単なる単純なクロージャです。

ECMAScript ではクロージャを次のように定義します。

  • 理論上、すべての関数はクロージャです。作成されたときに上位コンテキストのデータがすでに保存されているからです。
  • 実際には、クロージャは次の 2 つの条件を満たす必要があります。1. 外側のスコープの変数がコード内で参照されていること。2. クロージャが作成されたコンテキストが破棄されても、クロージャは存在し続けていることです。

JavaScript Definitive Guide の別のコードを見てみましょう。

スコープを「グローバルスコープ」にする
関数チェックスコープ(){
  スコープを 'ローカルスコープ' にします
  関数f(){
    戻りスコープ
  }
  戻り値 f
}

s = checkscope() とします。   
s() // これは何を返しますか?

多くの学生はそれがglobal scopeであると考えるかもしれませんが、本当にそうでしょうか? その実行プロセスを見てみましょう。

  • まず、グローバルコードを実行し、グローバル実行コンテキストを作成し、グローバル変数のscopeを定義して値を割り当てます。
  • checkscope関数を宣言し、関数の実行コンテキストを作成し、ローカル変数scopeを作成して値を割り当てます。
  • f関数を宣言し、関数の実行コンテキストを作成します。
  • checkscope関数を実行し、f関数を返して変数sに代入します。
  • s 関数を実行することは、f 関数を実行することと同じです。ここで返されるscopelocal scopeです。なぜlocal scopeなのかについては、語彙についてお話ししました。

スコープの基本ルール: JavaScript関数は、定義されているスコープを使用して実行されます。 f関数が定義されているスコープでは、変数scopeの値はlocal scopeである

5. 閉鎖の適用

クロージャは主に内部変数を維持するために使用されます。

6. クロージャの欠陥

  • クロージャの存在により変数がメモリ内に存在する可能性があるため、不適切な使用はメモリリークを引き起こす可能性があります。
  • メモリリークによりアプリがフリーズしたりクラッシュしたりする可能性がある

7. 閉会面接でよくある質問

var arr = []
(var i=0;i<3;i++){
    arr[i] = 関数(){
        コンソールログ(i)
    } 
}
arr[0]() // 3
arr[1]() // 3
arr[2]() // 3
// ここで、実行中に i は 3 になりました

// クロージャを使用して解決する var arr = []
(var i=0;i<3;i++){
    arr[i] = (関数(i){
        関数()を返す{
            コンソールログ(i)
        } 
    })(私)
    
}
arr[0]() // 0
arr[1]() // 1
arr[2]() // 2

Javascript のスコープとクロージャの詳細に関するこの記事はこれで終わりです。Javascript のスコープとクロージャの詳細については、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JS の難しさ 同期と非同期、スコープとクロージャ、プロトタイプとプロトタイプ チェーンの詳細な説明
  • JavaScript スコープ、スコープ チェーン、クロージャの使用方法の詳細な説明
  • JavaScript スコープクローズの詳細な説明
  • JS スコープとクロージャの詳細な理解
  • JavaScript 関数の使用方法の詳細な説明 [関数の定義、パラメータ、バインディング、スコープ、クロージャなど]
  • JS ページはセッション値、スコープ、およびクローズ スタディ ノートを取得します
  • JavaScriptはクロージャを使用してブロックレベルのスコープ操作をシミュレートします
  • JavaScript のスコープとクロージャ

<<:  CSSカウンター関連属性の学習の詳細な説明

>>:  Docker stopはすべてのコンテナを停止/削除します

推薦する

Dockerカスタムブリッジdocker0とdockerのコマンド操作の開始、終了、再起動

質問会社がサーバーを移行した後、デフォルトで作成された docker0 ブリッジが会社の外部ネットワ...

初心者がHTMLタグを学ぶ(3)

HTML に触れる初心者は、いくつかの HTML タグを学びます。関連記事:初心者が学ぶ HTML...

HTML テーブル マークアップ チュートリアル (42): テーブル ヘッダーの水平方向の配置属性 ALIGN

水平方向では、テーブル ヘッダーの配置を左、中央、右に設定できます。基本的な構文<TH ALI...

MySQL クイックデータ比較テクニック

MySQL の運用と保守において、R&D の同僚が 2 つの異なるインスタンスのデータを比較...

Mac で MySQL 8.0.22 のパスワードを取得する方法

Mac 最新バージョンの MySQL 8.0.22 パスワード回復問題の説明:昨日、突然、Macで最...

Docker Consul コンテナ サービスの更新と見つかった問題の概要

目次1. コンテナサービスの更新とDockerコンサルの検出1. サービス登録と検出とは何ですか? ...

mysql エラー 1033 を解決する方法: ファイル内の情報が正しくありません: 'xxx.frm'

問題の説明1. 収集ステーションのデータベース2. データが無い状態での移動は問題ありませんが、デー...

Vue で AES.js を使用する詳細な手順

AES暗号化の使用データ転送の暗号化と復号化処理 --- AES.js最初のステップ: vue に ...

Vueは複数列レイアウトドラッグを実装します

この記事では、マルチカラムレイアウトドラッグを実装するためのVueの具体的なコードを参考までに共有し...

MySQLクエリのソートとページング関連

概要通常、データベース内のデータを直接表示することは望ましくないため、最後の 2 つのセクションでは...

JavaScript でローカル変数をグローバル変数に変換する方法

まず関数の自己呼び出しを知る必要がある関数の自己呼び出し - 自己呼び出し関数1 回限りの関数 - ...

Linuxはデュアルネットワークカードボンドとドライバーインターフェースを使用する

債券とは何かNIC ボンドは、実稼働シナリオでよく使用されるテクノロジーです。複数の NIC を 1...

Nginx プロキシ転送構成を通じてクロスドメイン API プロキシ転送を実装する方法

序文WEB 開発では、クロスドメイン リクエストが頻繁に発生します。クロスドメインの問題を解決する方...

ubuntu18.04 での qt5.12.8 のインストールと環境設定に関する詳細なチュートリアル

環境システム: Ubuntu 18.04ソフトウェア: qt5.12.8 1. インストールパッケー...

Vue を使用して CSS トランジションとアニメーションを実装する方法

目次1. トランジションとアニメーションの違い2. Vueを使用して基本的なCSSトランジションとア...