ホームページ > ウェブフロントエンド > jsチュートリアル > JavaScriptを深く理解するシリーズ(12) 変数オブジェクト(Variable Object)_JavaScriptスキル

JavaScriptを深く理解するシリーズ(12) 変数オブジェクト(Variable Object)_JavaScriptスキル

WBOY
リリース: 2016-05-16 17:57:01
オリジナル
936 人が閲覧しました

JavaScript をプログラミングするとき、システムを正常に構築するには関数と変数の宣言を避けることはできませんが、インタプリタはこれらの関数と変数をどこでどのように見つけるのでしょうか?これらのオブジェクトを参照すると、正確には何が起こるのでしょうか?
元のリリース: Dmitry A. Soshnikov
公開時期: 2009-06-27
ロシア語のアドレス: http://dmitrysoshnikov.com/ecmascript/ru-chapter-2-variable-object/
英語翻訳: Dmitry A. Soshnikov
リリース時期: 2010-03-15
英語アドレス: http://dmitrysoshnikov.com/ecmascript/chapter-2-variable-object/
部分的に翻訳が困難
ほとんどの ECMAScript プログラマは、変数が実行コンテキストと密接に関連していることを知っているはずです:

コードをコピー コードは次のとおりです:

var a = 10 // グローバル コンテキストの変数
(function () {
var b = 20; // グローバル コンテキストのローカル変数function context
})();
alert(a); // 10
alert(b); // グローバル変数 "b" が宣言されていません

多くのプログラマー ご存知のとおり、現在の ECMAScript 仕様では、独立スコープは「関数」コード タイプの実行コンテキストを通じてのみ作成できると規定されています。つまり、C/C と比較して、ECMAScript の for ループはローカル コンテキストを作成できません。
コードをコピー コードは次のとおりです。

for (var k in {a: 1 , b: 2}) {
alert(k);
}
alert(k); // ループは終了しましたが、変数 k はまだ現在のスコープ内にあります

見てみましょう データを宣言したときに発見した詳細を見てみましょう。
データ宣言
変数が実行コンテキストに関連している場合、変数自体はデータがどこに保存されているか、またそのデータにアクセスする方法を知っている必要があります。この仕組みを変数オブジェクトと呼びます。
変数オブジェクト (VO と略します) は、実行コンテキストに関連する特別なオブジェクトです。
変数 (var、変数宣言);
FunctionDeclaration (FunctionDeclaration と略します); FD);
関数の仮パラメータ
たとえば、通常の ECMAScript オブジェクトを使用して変数オブジェクトを表すことができます:
コードをコピー コードは次のとおりです:

VO = {};
前述したように、VO は実行コンテキストのプロパティです:
activeExecutionContext = {
VO : {
//コンテキスト データ (var、FD、関数の引数)
}
};

グローバル コンテキスト変数オブジェクトのみが、間接的にアクセスする VO プロパティ名を渡すことができます。 (グローバル コンテキストでは、グローバル オブジェクト自体が変数オブジェクトであるため、後で詳しく説明します)、VO オブジェクトは内部メカニズムの実装にすぎないため、他のコンテキストでは直接アクセスできません。
変数または関数を宣言するとき、VO の新しい属性を作成するときと他に違いはありません (つまり、名前と対応する値があります)。
例:
コードをコピー コードは次のとおりです:

var a = 10;
function test(x) {
var b = 20;
test(30); 対応する変数オブジェクトは次のとおりです。


コードをコピー
コードは次のとおりです: // グローバル コンテキスト変数オブジェクトVO(globalContext ) = { a: 10,
test: <関数への参照>
}
// テスト関数コンテキストの変数オブジェクト
VO(test functionContext) = {
x: 30,
b: 20
};


特定の実装レベル (および仕様) では、変数オブジェクトは単なる抽象的な概念です。 (基本的に、特定の実行コンテキストでは、VO 名が異なり、初期構造も異なります。
異なる実行コンテキストの変数オブジェクト
すべてのタイプの実行コンテキストで、変数オブジェクトの一部の操作 (変数など)この観点から、変数オブジェクトを抽象的な基本的なものとして理解し、関数のコンテキストで変数オブジェクトに関連する追加の内容を定義することも容易になります。 🎜>コードをコピー

コードは次のとおりです:

抽象変数オブジェクト VO (変数初期化プロセスの一般的な動作)

╠══> グローバル コンテキスト変数オブジェクト GlobalContextVO
║ (VO === this === global)

╚══> 関数コンテキスト変数オブジェクト FunctionContextVO
(VO === AO、および追加された および <仮パラメータ>)

詳しく見てみましょう:
グローバル コンテキスト内の変数オブジェクト
まず、グローバル オブジェクトを明確に定義する必要があります。
グローバル オブジェクト (グローバル オブジェクト) は、実行コンテキストに入る前に作成されます。
このオブジェクトのコピーは 1 つだけあり、そのプロパティはプログラム内のどこからでもアクセスでき、グローバル オブジェクトのライフサイクルはプログラムが終了した時点で終了します。
コードをコピー
グローバル オブジェクトの最初の作成段階では、Math、String、Date、parseInt が独自の属性として使用され、他の属性も初期化されます。また、他の追加オブジェクトを属性として作成することもできます。グローバル オブジェクト自体を指すこともできます)。たとえば、DOM では、グローバル オブジェクトの window 属性はグローバル オブジェクト自体を参照できます (もちろん、すべての特定の実装がこのようになるわけではありません):
Copy code コードは次のとおりです:

global = {
Math: <...>,
String: <.. .>
...
...
window: global //自身を参照
};

プロパティにアクセスするとき、接頭辞は通常無視されます。グローバル オブジェクトには名前で直接アクセスできないためです。ただし、グローバル コンテキストの this を通じてグローバル オブジェクトにアクセスすることはできますし、それ自体を再帰的に参照することもできます。たとえば、DOM のウィンドウです。要約すると、コードは次のように省略できます:
コードをコピーします コードは次のとおりです:

String( 10); // global.String(10);
// プレフィックス付き
window.a = 10; global.a = 10 ;
this.b = 20; // global.b = 20;

つまり、グローバル コンテキストの変数オブジェクトに戻ります。 object はグローバル オブジェクトそのものです:
VO(globalContext) === global;
この原則に基づいて、対応関係がグローバル コンテキストで宣言されている場合にのみ、この結論を理解することが非常に必要です。グローバル オブジェクトのプロパティを通じて間接的にアクセスします (たとえば、事前に変数名がわからない場合)。
コードをコピー コードは次のとおりです。

var a = new String('test' );
alert(a); // 直接アクセス、VO(globalContext) で見つかります: "test"
alert(window['a']); // グローバル経由の間接アクセス: global === VO (globalContext) ): "test"
alert(a === this.a); // true
var aKey = 'a';
alert(window[aKey]);動的プロパティ名 アクセス: "test"

関数コンテキストの変数オブジェクト
関数実行コンテキストでは、現時点では VO に直接アクセスできません (アクティベーション オブジェクトによって再生されます)。略してAO) VOの役割。
VO(functionContext) === AO;
アクティブ オブジェクトは関数コンテキストに入るときに作成され、関数の argument 属性を通じて初期化されます。 argument 属性の値は Arguments オブジェクトです:
コードをコピー コードは次のとおりです:

AO = {
arguments:
};

Arguments オブジェクトは、次の属性を含むアクティブ オブジェクトの属性です。現在の関数への参照
length — 実際に渡されるパラメータの数
properties-indexes (文字列型の整数) プロパティの値は、関数のパラメータ値です (パラメータ内で左から右に配置されます)リスト)。 property-indexes 内の要素の数は、arguments.length と等しくなります。properties-indexes の値は、実際に渡されるパラメータと共有されます。
例:

コードをコピー コードは次のとおりです:

function foo(x, y, z) {
// 宣言された関数パラメータの引数の数 (x, y, z)
alert(foo.length) // 3
// 実際に渡されるパラメータの数 (x、y のみ)
alert(arguments.length); // 2
// パラメータの呼び出し先は関数自体です
alert(arguments.callee) === foo); // true
// パラメータ共有
alert(x === argument[0]); // true
alert(x); // 10
arguments[ 0] = 20;
alert(x); // 20
x = 30;
alert(arguments[0]); // 30
// ただし、パラメータ z はありません。渡されたパラメータは共有されません
z = 40;
alert(arguments[2]) // 未定義
arguments[2] = 50; / 40
}
foo(10, 20);

この例のコードには、パラメータ z、z が正しくない場合でも、Google Chrome の現在のバージョンにはバグがあります。渡され、arguments[2] は引き続き共有されます。
コンテキスト コードを処理する 2 つの段階
これで、ようやくこの記事の核心部分に到達しました。実行コンテキストのコードは、次の 2 つの基本的な処理段階に分割されます。
実行コンテキストの開始
コードの実行
変数オブジェクトの変更は、これら 2 つの段階と密接に関連しています。
注: これら 2 つの段階の処理は一般的な動作であり、コンテキストの種類とは関係ありません (つまり、パフォーマンスはグローバル コンテキストと関数コンテキストで同じです)。
実行コンテキストを入力します
実行コンテキストに入るとき (コード実行前)、VO には既に次の属性が含まれています (前述のとおり):
関数のすべての仮パラメータ (関数実行中の場合) context Medium)
- 名前と対応する値で構成される変数オブジェクトのプロパティが作成されます。対応するパラメーターが渡されない場合は、名前と未定義の値で構成される変数オブジェクトのプロパティも作成されます。
すべての関数宣言 (FunctionDeclaration、FD)
- 変数オブジェクトに同じ名前のプロパティが既に存在する場合、名前と対応する値で構成される変数オブジェクトのプロパティ (関数オブジェクト) が作成されます。この属性は完全に置き換えられます。
すべての変数宣言 (var、VariableDeclaration)
- 変数名が宣言された仮パラメータまたは関数と同じである場合、名前と対応する値 (未定義) で構成される変数オブジェクトのプロパティが作成されます。変数宣言は、このタイプの既存のプロパティを妨げません。
例を見てみましょう:

コードをコピー コードは次のとおりです:
function test( a, b) {
var c = 10;
function d() {}
var e = function _e() {};
(function x() {});
}
test(10); //

パラメータ 10 でテスト関数コンテキストに入ると、AO は次のように動作します:

コードをコピーします コードは次のとおりです:
AO(test) = {
a: 10,
b: 未定義,
c : 未定義、
d: <関数宣言 "d" への参照>
e: 未定義
};

AO には関数 " が含まれていないことに注意してください。 ×」。これは、「x」が関数宣言ではなく関数式 (FunctionExpression、略称 FE) であり、関数式は VO に影響を与えないためです。 いずれにせよ、関数「_e」も関数式ですが、後述するように変数「e」に代入されているため、「e」という名前でアクセスできます。 関数宣言 FunctionDeclaration と関数式 FunctionExpression の違いについては、第 15 章「関数」で詳しく説明します。名前付き関数式については、このシリーズの第 2 章も参照してください。
この後、コンテキスト コードの処理の第 2 段階、つまりコードの実行に入ります。
コード実行
このサイクル中、AO/VO はすでに属性を持っています (ただし、すべての属性が値を持っているわけではなく、ほとんどの属性の値はまだシステム デフォルトの初期値である未定義のままです)。
前の例と同様に、AO/VO はコード解釈中に次のように変更されました:

コードをコピーします コードは次のとおりです。次のように:
AO['c'] = 10;
AO['e'] =

もう一度注意してください。FunctionExpression "_e" は宣言された変数 "e" に保存されるため、メモリ内にまだ存在します。 FunctionExpression "x" が AO/VO に存在しません。つまり、"x" 関数を呼び出そうとすると、関数が定義される前でも後でも、エラー "x が定義されていません" が表示され、関数が呼び出されます。式は保存されません。式は、それ自体の定義内で、または再帰的にのみ呼び出すことができます。
もう 1 つの典型的な例:

コードをコピー コードは次のとおりです:

alert(x); // 関数
var x = 10;
alert(x); // 10
x = 20;
alert(x); // 20

最初のアラート「x」の戻り値が関数であり、「x」が宣言される前に「x」にアクセスするのはなぜですか?なぜ 10 や 20 ではないのでしょうか?仕様によれば、コンテキストに入るときに関数宣言が入力され、サイクルに一致するため、コンテキストに入るときに変数宣言 "x" もあり、前の段階で述べたように、変数宣言は次のようになります。 in sequence 関数宣言と仮パラメータ宣言の後、およびこのコンテキスト入力段階では、変数宣言は VO にすでに存在する同じ名前の関数宣言または仮パラメータ宣言を妨げません。したがって、コンテキストに入るとき、 VO の構造は次のとおりです:

コードをコピー コードは次のとおりです:
VO = {};
VO['x'] = < FunctionDeclaration "x" への参照>
// 関数 "x" が宣言されていない場合 <🎜; >// このとき、"x" の値は未定義である必要があります
// ただし、この場合の変数宣言は、同じ名前の関数の値には影響しません
VO['x'] = < ;値は乱れず、機能は維持されます>


次に、コード実行フェーズで、VO は次の変更を行います:


コード コードは次のとおりです: VO[' x'] = 10;
VO['x'] = 20;

この影響は 2 番目と 3 番目のアラートで確認できます。
以下の例では、変数がコンテキストフェーズ中に VO に入れられることが再度わかります。 (コードの else 部分は決して実行されませんが、とにかく変数 "b" が VO に存在するためです。)



コードをコピー } else {
var b = 2; 🎜>alert (a); // 1
alert(b); // 未定義、b が宣言されていないのではなく、b の値が未定義である


変数について
通常、JavaScript に関連するさまざまな記事や本はすべて、「(グローバル コンテキストで) var キーワードを使用しても、(どこでも) var キーワードを使用しなくても、変数を宣言できる」と主張しています。これは誤解であることに注意してください。
変数は常に、var キーワードを使用することによってのみ宣言できます。
上記の代入ステートメント:
a = 10;
これは、グローバル オブジェクトの新しいプロパティを作成するだけです (ただし、変数ではありません)。 「変数ではない」とは変更できないという意味ではなく、ECMAScript仕様の変数概念に準拠していないため「変数ではない」(グローバルオブジェクトのプロパティになり得る理由)これは完全に VO(globalContext) = == global のせいです。まだ覚えていますか?
次の例で具体的な違いを見てみましょう:




コードをコピーします

すべてのルートは依然として VO であり、コンテキスト フェーズとコード実行フェーズに入ります:
コンテキスト フェーズに入る:




コードをコピー


コードは次のとおりです:
VO = { a: unknown }; 「b」が次のとおりであることがわかります。変数ではありません。この段階では「b」はまったくありません。「b」はコード実行フェーズ中にのみ表示されます (ただし、この場合、エラーはその前に発生します)。
サンプル コードを変更してみましょう:




コードをコピーします


コードは次のとおりです:
alert( a); // 未定義、これは誰もが知っています、b = 10; // 10、コード実行フェーズ中に作成されます var a = 20; alert(a) ; // 20、コード実行フェーズ中の変更 変数に関する重要な知識ポイントがもう 1 つあります。単純な属性と比較して、変数には {DontDelete} という属性があります。この属性の意味は、変数属性を delete 演算子を使用して直接削除できないことです。



コードをコピー


コードは次のとおりです:

a = 10;
alert(window.a); // 10
alert(delete a); // true
alert(window.a); >var b = 20;
alert(window.b); // 20
alert(delete b); // false
alert(window.b); // 여전히 20 >
그러나 이 규칙은 하나의 컨텍스트, 즉 eval 컨텍스트에서는 왜곡될 수 없으며 변수에는 {DontDelete} 속성이 없습니다.


코드 복사 코드는 다음과 같습니다. eval('var a = 10;' );
alert(window.a); // 10
alert(delete a); // true
alert(window.a) // 정의되지 않음


이 인스턴스를 테스트할 때 일부 디버깅 도구(예: Firebug) 콘솔을 사용하는 경우 Firebug도 eval을 사용하여 콘솔에서 코드를 실행한다는 점에 유의하세요. 따라서 변수 속성에도 {DontDelete} 속성이 없으므로 삭제가 가능합니다.
특수 구현: __parent__ 속성 ​​
앞서 언급했듯이 표준 사양에 따르면 활성 개체에 직접 액세스할 수 없습니다. 그러나 SpiderMonkey 및 Rhino와 같은 일부 특정 구현은 이 규정을 완전히 준수하지 않습니다. 구현 시 함수에는 특수 속성 __parent__이 있으며 이를 통해 함수가 생성한 활성 개체 또는 전역 변수 개체를 직접 참조할 수 있습니다.
예(SpiderMonkey, Rhino):



코드 복사 코드는 다음과 같습니다. var global = this;
var a = 10;
function foo() {}
alert(foo.__parent__); // global VO = foo.__parent__; Alert(VO.a); // 10
alert(VO === global); // true


위의 예에서 foo 함수가 전역에서 생성되는 것을 볼 수 있습니다. 따라서 __parent__ 속성은 전역 컨텍스트의 변수 객체, 즉 전역 객체를 가리킵니다.
그러나 SpiderMonkey에서는 동일한 방식으로 활성 개체에 액세스할 수 없습니다. 다른 버전의 SpiderMonkey에서는 내부 함수의 __parent__가 때때로 null을 가리키기도 하고 때로는 전역 개체를 가리키기도 합니다.
Rhino에서는 동일한 방식으로 활성 개체에 액세스하는 것이 완전히 가능합니다.
예(Rhino):




코드 복사
코드는 다음과 같습니다. var global = this ; var x = 10; (function foo() {
var y = 20;
// "foo" 컨텍스트의 활성 개체
var AO = (함수 () { }).__parent__;
print(AO.y); // 20
// 현재 활성 객체의 __parent__는 기존 전역 객체입니다.
// 변수 객체의 특수 체인
// 그래서 이를 범위 체인이라고 부릅니다.
print(AO.__parent__ === global); // true
print(AO.__parent__.x) // 10
})() ;


요약
이번 글에서는 실행 컨텍스트와 관련된 객체를 심층적으로 살펴보았습니다. 이 지식이 귀하에게 도움이 되기를 바라며 귀하가 직면한 몇 가지 문제나 혼란을 해결할 수 있기를 바랍니다. 계획대로 다음 장에서는 범위 체인, 식별자 확인 및 클로저를 살펴보겠습니다.
궁금한 점이 있으시면 아래 댓글로 답변해 드리겠습니다.
기타 참고자료


10.1.3 –
변수 인스턴스화
;
10.1.5 –
関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート