概要
- クロージャの概念、スコープチェーンの概念、上位スコープの決定方法、実行可能なコンテキスト環境
- 関数呼び出しスタックが破壊されないクロージャーメカニズムを構成する条件
- クロージャのメリットとデメリット
- コードの分析、描画メモリ グラフ分析、Chrome コンソール ブレークポイント デバッグ分析
- 閉包解析を形成しません
- クロージャーメカニズムを使用してタブタブを実装する
- 私の個人的な知識には限界がございますので、もし記事に誤りがございましたら、アドバイスをいただければ幸いです。
クロージャコンセプト
私の個人的なクロージャの概念: 関数呼び出しは破棄できないプライベート スコープを形成し、ローカル変数を外部干渉から保護し、スコープ チェーン検索メカニズムを使用して後で使用するローカル変数を実装します。このメカニズムがクロージャです。
スコープチェーン検索メカニズム
実行可能なコンテキスト コール スタック FECS は、関数が呼び出されるときに形成されます。変数がコール スタック内で使用される場合、まず現在のコール スタック内から検索されます。見つかった場合は、検索は停止され、直接使用されます。現在の呼び出しスタックにそのようなローカル変数がない場合は、上位レベルのスコープで検索を続けます。特定のスコープで見つかった場合は、検索を停止して直接使用します。それ以外の場合は、グローバル オブジェクト ウィンドウが見つかるまで続けます。 。このレイヤーごとの検索メカニズムがスコープ チェーン検索メカニズムです。
実行可能コンテキスト
JS プログラミング言語には、
全局可执行上下文环境GEC、函数调用形成的函数可执行上下文环境FEC
eval 呼び出しによって形成される実行可能コンテキストのみが存在します。実行コンテキストの最大の役割はコードの実行ですが、とりあえずはJSコードの実行を提供するスタックメモリ空間であると単純に理解していただければと思います。
親スコープを決定する方法
関数の上位スコープは、関数が宣言および定義される場所であり、関数が呼び出される場所とは関係ありません。
個人的には、クロージャ メカニズムを構成する条件が、関数呼び出しスタックが破壊されないことにつながると思います。
1) 関数の戻り値は参照型データです。2) この参照型データは、実行時にコンテキスト実行可能環境呼び出しスタックを
生成できなければなりません。または、この参照型データの特定の属性が実行される必要があります。3) 外部スコープ変数は関数から返される参照型データを受け取ります。上下文可执行环境调用栈
クロージャのメリットとデメリット
利点: ローカル変数は外部スコープの干渉から保護されており、後で使用できます。
欠点: 使用後に手動でバインドを解除しないと、GC ガベージ コレクターがリサイクルできないため、メモリ リークが発生しやすくなります。大量に使用すると、パフォーマンスが大幅に消費され、プログラムの実行が遅れたり、クラッシュしたりすることがあります。
メモリグラフを描いて関数呼び出しスタックが破壊されていない状況を解析する
1. 次のコードを分析します。返回值是函数,函数调用时候可以生成可执行上下文环境调用栈
function foo (num1) {
return function baz (num2) {
return ++num1 + num2
}
}
let bar = foo(100)
console.log(bar(2)) // 103
console.log(bar(2)) // 104
foo
コールスタックが破壊されないことの分析は次のとおりです。1
)一時保存ローカル変数を函数foo调用时候,代码执行之前
作成し、ローカル変数を初期化します。また、範囲も決めます。わかりました。これらは一緒になって、関数が呼び出されたときに形成されるコンテキスト実行可能環境を形成し、AO オブジェクトに関連付けられます。また、この段階で実行され、現在のスコープで宣言された変数がプロモートされ、宣言された関数がプロモートされます。2) 関数 foo 呼び出しスタックの js コードが実行され、AO ペアの属性 num1:100 が変更されます。作成したオブジェクトヒープメモリアドレスを受信用外部変数barに返します。このとき、外部変数 bar 参照は 0x12ff8899 を指します。JS プログラミング言語では、3) 通常、関数 foo が呼び出された後、スタック メモリが破棄され、ao オブジェクトも破棄されます。ただし、戻り値は現在のプライベート スコープで作成された参照型データであるため、このオブジェクトは外部スコープの変数 bar 参照によってポイントされます。。同時に、関数 bar が呼び出されないときは、関数 foo 呼び出しスタックのプライベート変数が使用されるかどうかを決定できません。また、関数 bar が呼び出されたときに、コードが実行される前に、bar関数のスコープ チェーンは上位レベルのスコープを指します。したがって、現時点では関数 foo のスタックメモリ空間を一時的に破壊することはできません。4) 関数 bar(2) が初めて呼び出されるとき、コンテキスト実行可能環境の呼び出しスタックも形成されます。ローカル変数を保存するための AO オブジェクトを作成します。コードを実行する際には変数が使用されますが、スコープチェーン検索の仕組みにより、まず現在のスコープで検索され、変数が存在しない場合は上位スコープの関数 foo で検索が続けられます。関数 foo は、それを ao オブジェクトのデータに関連付けます。AO对象
{num1:undefined,baz:0x12ff8899}
作用域链
this
AO、作用域链、this
预处理机制
var
function
声明+定义
num1=100
0x12ff8899
函数也是对象
更加重要的是这个对象是函数,调用时候会形成上下文可执行环境
作用域链
函数foo调用栈
因为函数的上级作用域仅仅和定义位置有关,和调用位置无关
{num2:undefined}
num1
num1:100
スタックメモリにロードします。++num1 演算の結果は です101
。次に、 foo 関数に関連付けられた ao オブジェクトに 101 を格納します{num1:101}
。bar 関数の最終的な戻り値は 103 です。このとき、 bar 関数の戻り値は one であるため基本类型数据
、コードの実行終了後、 bar によって形成されたスタック メモリ空間は破棄され、関連付けられていた ao オブジェクトも破棄されます。しかし函数foo调用栈还不会销毁
。
メモリ グラフ分析
Chrome コンソール メモリ デバッグ分析
1) 最初のブレークポイント デバッグ
2) 2 番目のブレークポイント デバッグ分析
2. 次のコードを分析します。返回值是普通对象,这个普通对象含有方法,方法调用时候也会生成可执行上下文调用栈
function baz () {
let msg = '闭包好难理解啊'
let count = 0
return