概要:必要次の記事を読む15 Fenzhongを
質問アスカーはJavaScriptをメモリ内にどのように割り当てられ、導入の過程でメートル含まJSをしてスコープチェーンと関数呼び出しコールが生成さlexicial環境と環境レコード(と呼ばれる著者の合併バインディングオブジェクト)プロセスを。まあ価値があります見て
個人の翻訳:
どのように変数は、JavaScriptでメモリを割り当てられていますか?
それは実際にはJavaScriptの非常に興味深い分野だし、少なくとも2つの答えがあります。
- 何の面で答え 仕様が 定義し、
- 最適化された(そして多くの場合である)することができるJavaScriptエンジンが実際に何をすべきかという点で答え、
実際には、これはJavaScriptコンテンツの一部であり、非常に興味深いです。あなたは、特定の仕様を確認することができ、
ここでは、2つの方向は、その答えを参照されます。
- それがどのように定義されるかという点で仕様..
- 特定の条件でJSエンジン..が行う方法である(これらのエンジンはまた、一般的に最適化されたコンテンツの数が含まれています)
仕様の面では:ローカル変数を扱うのJavaScriptの方法は、Cはそれをしない方法とは全く異なります。あなたは、とりわけ機能、呼び出すと 字句環境 に何かが呼ばれたその呼び出しのために作成され、 環境のレコードを。物事をシンプルに保つために、私は彼らが仕様内の別々のだ正当な理由はしかし、あります(「バインディングオブジェクト」として一緒に両方を参照するつもりです。あなたはそれに深く取得したい場合は、数時間を取っておきますそして、)仕様をお読みください。
標準化された用語では、JavascriptがC言語のローカル変数を処理し、いくつかの方法は、あなたが(呼び出し)関数を呼び出すときと同じではありません
これは、字句の環境を作成し、
内部環境レコードが含まれます、
簡単にするために、私は「バインディング・オブジェクト」と呼ばれる彼らと合併ます(仕様で、彼らはあなたがより良い理解をしたい場合、あなたは自分自身を調整するために行くことができ、別のものです。)
バインディングオブジェクトが含まれている バインディング (他のもののカップルと一緒に)関数の引数、関数内で宣言されたすべてのローカル変数、関数内で宣言されているすべての機能のために。
バインディングオブジェクトが含まれ 、バインディングを(も何か他のものが含まれている)関数の引数、関数定義内のローカル変数、関数定義と内部の機能を含みます
---翻訳:人はも言いませんでしたが、私はそれがまた、これを含めるべきだと思います
バインディングは、 名前(のような組み合わせである a
)と結合(フラグのカップルと一緒に私たちはここを心配する必要はありません)の現在の値。
バインディングバインディング名と現在の値のセットを指します
関数内の非修飾参照(例えば、 foo
で foo
はなく、 foo
で obj.foo
修飾されている、)最初に、それはそれに結合と一致するかどうかを確認するために結合オブジェクトに対してチェックされます。それがない場合は、それが使用されているバインディング。
(例えば代わりobj.foo参照のfooへの直接参照、探しなど)が見つかりません参照し、それはこのバインディングを使用する場合、どこ見つけるに結合するオブジェクトを開始します
クロージャは、(いくつかの理由で発生する可能性があります)を返す関数を存続する場合、その関数呼び出しのバインディングオブジェクトがされて 保持さ クロージャは、それが作成された場所にバインドオブジェクトへの参照を持っているので、メモリに。仕様面でだから、それはすべてのオブジェクトについてです。
フィニッシュ後の関数の戻り値は、閉鎖(閉鎖)を保持した場合の閉鎖は、バインディングオブジェクトを引用しているため、バインディングオブジェクトによって生成された関数呼び出し(コール)は、メモリに保持されます。ですから仕様、これらの変数彼らがオブジェクトで宣言されている(それはオブジェクトに関するすべてである - 注釈:ここでのオブジェクトを参照し、一般的なオブジェクトでのC / C ++オブジェクトは、ヒープメモリに保存されています)。
一見すると、それはスタックがローカル変数に使用されていないことを示唆しています。実際には、近代的なJavaScriptエンジンは非常にスマートであり、(それは価値がある場合)、実際に閉鎖によって使用されていない地元の人々のためのスタックを使用することができます。彼らは地元の人々のためにスタックを使用することができ ない 閉鎖によって慣れるが、その後、機能はとても閉鎖はそれらへのアクセスを持ち続けて戻ったときに結合オブジェクトに移動します。(当然のことながら、スタックは、まだリターンアドレスなどを追跡するために使用されます。)
ローカル変数は(それが参照されるようにしてもメモリヒープの後に続いている)スタックに格納されていないよう。実際には、近代的なJavaScriptエンジンが非常に高い非常にスマートであり、スタックを使用することが可能であるような外観、ローカル変数を格納するための閉鎖を言及されていないが、関数が結合オブジェクトに戻ったときに、クロージャは、それらへのアクセスを継続できるように、それらを移動します(もちろん、まだスタック関数の戻りアドレスを追跡するために使用すること。)
ここでは例を示します。
関数foo(a、b)は{
C;
C = A + B。
機能バー(D){
警告( "Dの*のC =" +(Dの*のc)参照)。
}
バーを返します。
}
VaRのB = FOO(1、2)。
B(3)。//アラート "のD *のC = 9"
私たちが呼び出すと foo
、バインディングオブジェクトはこれらのバインディング(仕様に従って)で作成されます:
a
そして、b
-関数の引数c
- 関数内で宣言されたローカル変数bar
- 関数内で宣言された関数- (...と他の物事のカップル)
FOOを呼び出すときにこれらのバインディングを持っているバインディングオブジェクトを作成します:
- AとBの - 関数の引数)
- ローカル変数内で定義されたC-関数
- バー - 内の関数定義機能
- (...といくつかの他のもの)
ときに foo
ステートメントを実行し c = a + b;
、それが参照するだ c
、 a
と b
にその呼び出しのためのバインディングオブジェクトのバインディングを foo
。
とき foo
に参照を返し bar
、その中に宣言された関数を、 bar
呼び出しを存続 foo
返します。ため bar
に、その特定の呼に対する結合オブジェクトに(隠れた)基準を有している foo
(通常の場合には、そこに未処理の参照ないであろうし、それは、ガベージコレクションのために利用可能であるのに対して)、結合オブジェクトが生き残ります。
時間; C = A + BにFOOを行う場合
Cの結合オブジェクト参照、これらの結合の、B
fooはタイムバーへの参照を返す場合、バーは結合オブジェクトへの参照を持っているので、結合オブジェクトが保持されているので、バーは、保存される(通常の状況下で参照されていない、それは、ガベージコレクタであるべきです)
その後、私たちが呼ぶとき bar
、 新しい そのコールの結合オブジェクトが呼び出されるバインディング(とりわけ)を使用して作成された d
引数を- bar
。その新しいバインディングオブジェクトを取得します 親 に取り付けられた1つ:バインディングオブジェクトを bar
。彼らは一緒に「スコープチェーン」を形成します。
内の非修飾参照は、 bar
最初にその呼び出しのバインディングオブジェクトに対してチェックされ bar
、たとえば、よう、 d
に解決 d
への呼び出しのためのバインディングオブジェクトにバインド bar
。
私たちはバー、および結合Dと呼ばれるオブジェクト結合、新世代の呼び出すときに、 - パラメータのバーを、バインディングオブジェクトの父は、元のバインディングのオブジェクトである、彼らは一緒に「スコープチェーン」スコープチェーンを形成しました。例えば、dが解析されるための機能が、予約バーで参照されていない検索するバインディング日間となったが、
しかし、そのバインディングオブジェクトのバインディングと一致していない未修飾の参照はその後、その後の呼び出しのための結合対象であるスコープチェーン内の親オブジェクト結合、照合され foo
作成されています bar
。それがためにバインディングがあるので c
、それは識別子のために使用されるバインディングです c
内 bar
。例えば、ラフな面で:
Cバインディングその後、使用するC識別子内バールであったように、しかし、結合オブジェクトが参照されている中で、次は彼の父を見つけるために、その範囲の鎖結合オブジェクトの上になり、オブジェクトfooを結合することは、Cがある見つけることができません大まかに言えば:
+ --------------------------- +
| グローバルバインディングオブジェクト|
+ --------------------------- +
| .... |
+ --------------------------- +
^
| 鎖
|
+ --------------------------- +
| `foo`コールバインディングオブジェクト|
+ --------------------------- +
| = 1 |
| B = 2 |
| C = 3 |
| バー=(機能)|
+ --------------------------- +
^
| 鎖
|
+ --------------------------- +
| `bar`呼び出しオブジェクトを結合|
+ --------------------------- +
| D = 3 |
+ --------------------------- +
楽しい事実:このスコープチェーンはグローバル変数をJavaScriptでどのように動作するかです。上記の「グローバルバインディングオブジェクトを」注意してください。だから、関数内で、グローバルバインディングオブジェクトが結合している場合は、その関数呼び出しのための結合物ではないが、それとグローバルバインディングオブジェクト間の他の結合オブジェクトのいずれかにない識別子を使用している場合そのために、世界的な結合が使用されています。ほら、グローバル変数。
スコープチェーンはグローバル変数が「結合オブジェクトをgloball」ラベル付けされているJavaScriptの作品であります
あなたが生産関数呼び出しでバインディングオブジェクト識別子を使用しますが、そうでない場合は、グローバルバインディングオブジェクトまで、機能に見上げてきたでしょう。
昔ながらの世界のような宣言によって使用される層(ES2015は、グローバル・バインディング・オブジェクトへの2つの層を有することによって、このビットより興味深いを行い var
、関数宣言、など新しいものによって使用される層 let
、 const
及び、 class
。
違いは、古い層も経由してあなたのアクセスの種類グローバルオブジェクトのプロパティを、作成することです window
ブラウザ上のが、新しい層にはありません。だから、グローバル let
宣言は作成されません window
プロパティを、しかし、世界的な var
宣言がありません。)
いくつかのこのより興味深い部分を作るためにグローバルバインディングオブジェクトに2を加えることにより、ES2015
一つは、varと機能の宣言として古いスタイルのグローバル宣言、constのクラスイメージを聞かせて更新して別のものを使用しています。
違いは、古い層はグローバルオブジェクトのプロパティを作成し、あなたがブラウザ介しことができるということです窓をアクセスする、しかし新しいものにすることはできません
だから、グローバル宣言は、ウィンドウ内に作成されませんしましょう、しかし、グローバルvarステートメントは、プロパティウィンドウを作成します
実装は、上記を作るためにカバーの下に、彼らが好きなメカニズム自由に使用することが 思える 発生します。
これは、関数呼び出しのバインディングオブジェクトへの直接アクセスを取得することは不可能だし、スペックは、バインディングオブジェクトは単なる概念ではなく、実装のリテラルの一部である場合、それは完全に罰金だことが明らかになります。
特定の実装は、エンジンの実装機構に基づくであろうことは異なっています
バインディングオブジェクトは、関数呼び出しを取得するために直接生成することはできません
そして、仕様があり、実際にこの概念の結合オブジェクトをすることができている、とメカニズムについては具体的な詳細を達成しないと言います
単純な実装はよく文字通り仕様の言うことを行うことができます。より複雑なものは、(スピード利益のために)関与なし閉鎖がない場合、スタックを使用することができ、または常にスタックを使用することができますが、その後、スタックをポップするとき閉鎖するために必要なバインディングオブジェクトを「はがします」。任意の特定の場合に知るための唯一の方法は、自分のコードを見ることです。:-)
それはこれらのクロージャに来るとき、あなたがエンジンのコードを見ていない限り、文字通り仕様を尽くすような単純な実装は..スタックの導入を最適化するために、より複雑になります。
等の閉鎖、スコープチェーン、詳細についてはこちら:
- クロージャは複雑されていません (多少日付の用語のうちの)
- 悪い誤解「VAR」
インフォメーション:lexial環境上のショットのための仕様