ホームページ > ウェブフロントエンド > jsチュートリアル > JavaScript オブジェクト モデルの実行モデル_javascript スキル

JavaScript オブジェクト モデルの実行モデル_javascript スキル

WBOY
リリース: 2016-05-16 18:18:39
オリジナル
829 人が閲覧しました
データ型
基本データ型
基本データ型は、JS 言語の最も低い実装です。
単純な数値型: 未定義、Null、ブール、数値、文字列。ここでの説明にある英語の単語はデータ型の名前のみを指しており、JS のグローバル オブジェクト (an、Boolean、Number、String など) を具体的に指すものではないことに注意してください。これらの概念の違いは比較的大きいです。
オブジェクト: 値が単純な数値型、オブジェクト、または関数である順序のない属性のコレクション。上記と同様に、ここでのオブジェクトはグローバル オブジェクト Object を具体的に指すものではありません。
関数: 関数はオブジェクトのタイプです。実装では、内部属性 [[Class]] の値は「Function」であり、オブジェクトの内部属性メソッドに加えて、 [[Construct]]、[[Call]]、[[Scope]] などの内部属性も含まれます。関数呼び出しとしての関数の処理メカニズムは、コンストラクターの処理メカニズム (new キーワードを使用してインスタンス オブジェクトを作成する) とは異なります (Function オブジェクトを除く) 内部メソッド [[Construct]] を使用してロジックをコンストラクターとして実装します。メソッド [[Call]] は、ロジックを関数呼び出しとして実装します。上記と同様に、ここでの関数はグローバル オブジェクト Function を具体的に参照するものではありません。
関数は、JS のプロトタイプ言語におけるオブジェクト指向言語のクラスとみなすことができ、オブジェクトのインスタンスを構築するために使用できます。関数はクラスとして見ることができるため、各関数は拡張データ型として見ることができます。

組み込みデータ型 (組み込みオブジェクト)
関数: 関数型のユーザー インターフェイス。
オブジェクト: オブジェクト タイプのユーザー インターフェイス。
Boolean、Number、String: これら 3 つの単純な数値型のオブジェクト ラッパーは、概念的には C# の Box/Unbox に似ています。
Date、Array、RegExp: これらは、いくつかの組み込み拡張データ型とみなすことができます。

まず第一に、Function、Object、Boolean、Number、String、Date、Array、RegExp などはすべて JavaScript 言語の組み込みオブジェクトであり、すべて関数の派生型とみなすことができます。たとえば、関数のインスタンスの数は true です。オブジェクトのインスタンスの数は true です。この意味では、これらはユーザー定義関数と同じように扱うことができます。
第 2 に、それぞれはデータ型を表すことができ、ネイティブ コードまたは組み込み JS コードを使用して JS エンジンによって実装され、これらの組み込みデータ型を操作するために開発者に公開されるインターフェイスです。この意味で、それらは抽象的な概念であり、その背後に特定の実装メカニズムが隠されています。
数値、関数などの単語に言及するたびに、それらを頭の中で上記の 2 つの状況のいずれかに即して具体化する必要があります。

データ型実装モデルの説明
JavaScript オブジェクト モデルの実行モデル_javascript スキル
ビルトイン *** データ構造: *** 型を実装するために JS で内部的に使用されるデータ構造を指します。これらの構造には、基本的には直接アクセスできません。
ビルトイン *** オブジェクト: JS のビルトイン Number、String、Boolean およびその他のオブジェクトを指します。これは、JS が内部実装されたデータ型を開発者に公開するためのインターフェイスです。
ビルトイン *** コンストラクター: JS に組み込まれたいくつかのコンストラクターを指し、対応する型のオブジェクト インスタンスを構築するために使用されます。これらは関数オブジェクトとしてパッケージ化され、公開されます。たとえば、次のメソッドを使用してこれらの関数オブジェクトにアクセスできます。
コードをコピーコードは次のとおりです:

//FF2.0、IE7、Opera9.25、Safari3.0.4で渡されます
//組み込みの数値コンストラクターにアクセスします
varnumber = new Number(123) ;
var numConstructor1 = number.constructor; // または
var numConstructor2 = new Object(123).constructor;
// numConstructor1 と numConstructor2 は両方とも組み込みの Number コンストラクターです
numConstructor1 == numConstructor2 //結果: true
//組み込みオブジェクト コンストラクターにアクセスします
var objConstructor1 = {}.constructor; //または
var objConstructor2 = new Object().constructor; 🎜>// objConstructor1 と objConstructor2 はどちらも組み込みの Object コンストラクターです
objConstructor1==objConstructor2 //result: true

特定の実装に関しては、上図の横方向、例えばbuild-Inのデータ構造やコンストラクタ、Function、Date、Array、RegExpなどはObjectの構造を継承して実装できますが、これは具体的な実装に関する事項です。 。

単純な数値型のオブジェクト化について
これは微妙な点ですが、次の説明は Boolean、String、Number の 3 つの単純な数値型に当てはまります。例として Number を取り上げます。
JS 仕様の要件: 基本データ型を直接返すには、var num1=123; のようなコードを使用します。これは、返されるオブジェクトが Number および Object 型から派生したものではないことを意味します。数値型を返します。例: var num2=new Number(123); num2 instanceof Number は true です。
Number を関数として呼び出すと、戻り結果が単純な数値型に変換されます。以下はテスト コードです:

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

//FF2.0、IE7、Opera9.25、Safari3.0.4 で渡されます
var num1 = new Number(123); //Number と Object から派生した num1
num1instanceof Number //result: true
num1instanceof Object //result: true
//num1 を Number 型からプリミティブ型に変換します。 Number または Object のインスタンス
num1 = Number(num1);
num1instanceofNumber //結果: false
num1instanceof Object //結果:false
var num2 = 123; //num2 はプリミティブ型です
num2instanceofNumber //結果: false
num2instanceof Object //結果: false

単純な数値型を取得しましたが、Object と、対応する型のすべてのプロパティとメソッドを備えた JS オブジェクト オブジェクトのように見えます。基本的には、instanceof のテスト結果が異なります。 。

プロトタイプの継承
プロトタイプ
各オブジェクトには [[Prototype]] の内部プロパティがあり、その値は null または別のオブジェクトです。関数オブジェクトには明示的なプロトタイプ属性がありますが、これは内部 [[Prototype]] 属性ではありません。さまざまな JS エンジン実装者は、内部 [[Prototype]] 属性に任意の名前を付け、その可視性を JS エンジン内でのみ使用されるように設定できます。内部 [[Prototype]] には JS コードではアクセスできませんが (FireFox ではアクセスできます。Mozilla が公開しているため、名前は __proto__ です)、オブジェクトの isPrototypeOf() メソッドをテストに使用できることに注意してください。判定はプロトタイプチェーンで実行されます。
obj.propName を使用してオブジェクトのプロパティにアクセスする場合は、次の手順に従います (obj の内部 [[Prototype]] プロパティの名前が __proto__ であると仮定します):
1. obj に propName プロパティがある場合は、プロパティの値、それ以外の場合は
2. obj.__proto__ が null の場合は、未定義を返します。それ以外の場合は、
3. obj.__proto__.propName を返します。
オブジェクトの呼び出し方法は、属性検索プロセスにアクセスする場合と同じです。なぜなら、メソッド An object の関数はオブジェクトの属性値であるからです。
ヒント: 上記の手順は再帰的なプロセスを意味します。手順 3 では、obj.__proto__ も propName 属性の検索に使用されます。

たとえば、次の図に示すように、object1 には属性 prop1、prop2、prop3 とメソッド fn1、fn2、fn3 があります。図の点線の矢印はプロトタイプ チェーンを表します。
JavaScript オブジェクト モデルの実行モデル_javascript スキル
Prototypeに基づく継承と共有です。 object1 のメソッド fn2 は object2 から派生したものであり、概念的には、object2 は object3 のメソッド fn2 をオーバーライドします。
JavaScript オブジェクトはすべて、プロトタイプ チェーンを通じて関連付けられている必要があります。最上位は Object です。つまり、オブジェクトはすべて Object 型から派生します。

C などのオブジェクト指向言語は、メソッドを運ぶためにクラス (抽象型) を使用し、属性を運ぶためにオブジェクト (インスタンス化されたオブジェクト) を使用します。プロトタイプ言語は、メソッドと属性を運ぶためにインスタンス化されたオブジェクトのみを使用します。本質的な違いは、前者はメモリ構造の記述に基づいて継承を実装するのに対し、後者は特定のメモリ ブロックに基づいて継承を実装することです。

オブジェクトの作成手順
JS では関数オブジェクトのみクラスの概念があるため、オブジェクトを作成するには関数オブジェクトを使用する必要があります。関数オブジェクト内には [[Construct]] メソッドと [[Call]] メソッドがあり、 [[Construct]] はオブジェクトの構築に使用され、 [[Call]] は関数の呼び出しにのみ使用されます。 new 演算子を使用する場合。
var obj=new Object(); は、組み込みの Object 関数 object を使用して、インスタンス化されたオブジェクト obj を作成します。 var obj={}; および var obj=[]; この種のコードは、JS エンジンによるオブジェクトと配列の構築プロセスをトリガーします。 function fn(){}; var myObj=new fn(); は、ユーザー定義型を使用してインスタンス化されたオブジェクトを作成します。

新しい Fn(args) の作成プロセスは次のとおりです (つまり、関数オブジェクトの [[Construct]] メソッドがロジックとオブジェクトの作成プロセスを処理します)。また、関数オブジェクト自体の作成処理(関数を定義したり、Functionを使って関数オブジェクトを作成することなどを指します)も以下の処理ロジックを使用しますが、特殊な箇所がありますので、それについては後述します。
1. 組み込みオブジェクト object obj を作成し、それを初期化します。
2. Fn.prototype が Object 型の場合、obj の内部 [[Prototype]] を Fn.prototype に設定します。それ以外の場合、obj の [[Prototype ]] はその値 (つまり、Object.prototype) を初期化します。
3. obj をこのように使用し、args パラメーターを使用して Fn
の内部 [[Call]] メソッドを呼び出します。3.1 内部 [[Call]] メソッドは、現在の実行コンテキスト
3.2 F の関数本体を呼び出します
3.3 現在の実行コンテキストを破棄します
3.4 F の関数本体の戻り値を返します。F の関数本体に戻り値がない場合は、戻り値が返されます。未定義
4. [[ Call の戻り値の場合]] が Object 型の場合は、この値が返され、そうでない場合は、obj が返されます。手順 2 では、prototype は、オブジェクト、および [[Prototype]] はオブジェクトの内部プロトタイプ プロパティを表します (暗黙的)。
オブジェクトのプロトタイプ チェーンを構成するのは、内部の暗黙的な [[Prototype]] であり、オブジェクトの明示的なプロトタイプ属性ではありません。表示されるプロトタイプは、関数オブジェクト上でのみ意味を持ちます。上記の作成プロセスからわかるように、関数のプロトタイプは、プロトタイプに従って、暗黙的に派生オブジェクトの [[Prototype]] 属性に割り当てられます。ルール、派生オブジェクトのプロトタイプオブジェクト、関数の間には、プロパティとメソッドの継承・共有関係のみが存在します。

コードを使用して検証を行います:

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

//FF2.0、IE7、Opera9.25、Safari3.0.4 で渡されます
function fn(){}
//それらのオブジェクトの暗黙的な [[Prototype]] プロパティの値fn から派生したものは fn.prototype
fn.prototype={ attr1:"aaa", attr2:"bbb"}; に割り当てられます。
var obj=new fn();
document.write(obj.attr1 "
"); //結果: aaa
document.write(obj.attr2 "
"); //結果: bbb
document.write(obj instanceof fn); //結果: true
document.write("
");
//ここで fn のプロトタイプを変更するので、Prototype のアルゴリズムにより、obj は fn のインスタンスではなくなります。
//ただし、これは obj とその [[Prototype]] プロパティには影響しません。そして、obj には attr1 プロパティと attr2 プロパティがまだあります
fn.prototype={};
document.write(obj.attr1 "
"); //結果: aaa
document.write(obj.attr2 "
"); //結果: bbb
document.write(obj instanceof fn); //結果: false 作成手順の戻り値に関するテスト:
//FF2.0、IE7、Opera9.25、Safari3.0.4 で渡されました
function fn(){
//ステップ 4 による上で説明したように、
//新しい fn() オペレーションはオブジェクト { attr1: 111, attr2: 222 } を返しますが、これは fn のインスタンスではありません。
return { attr1: 111, attr2: 222 };
}
fn.prototype={ attr1:"aaa", attr2:"bbb"};
var obj=new fn();
document.write(obj.attr1 "
"); //結果: 111
document.write(obj.attr2 "
"); //結果: 222
document.write(obj instanceof fn); //結果: false


の上の理解を通過し、下にこの大きな図の実現コードを書き込みます。 図中の CF は関数、Cfp は CF のプロトタイプ オブジェクトです。 、cf1、cf2、cf3、cf4、cf5 はすべて CF のサンプル オブジェクトです。 点線の枠はプロトタイプの関係を示し、線の枠はプロトタイプの表示を示します。 >
JavaScript オブジェクト モデルの実行モデル_javascript スキル

复制代码
代码如下: //FF2.0、IE7、Opera9.25で渡されます。 Safari3.0.4 関数 CF(q1, q2){
this.q1=q1;
this.q2=q2;
}
CF.P1="CF の P1";
CF.P2="CF の P2";
function Cfp(){
this.CFP1="CFP の CFP1";
}
CF.prototype=new Cfp();
var cf1=new CF("aaa", "bbb");
document.write(cf1.CFP1 "
"); //結果: CFP1 の Cfp
document.write(cf1.q1 "
"); //結果: aaa
document.write(cf1.q2 "
"); //結果: bbb


本地プロパティと継承プロパティ
オブジェクトは、隐式 Prototype を介してプロパティとメソッドの継承が可能ですが、prototype も普通のオブジェクトです。
では、オブジェクトのプロパティを設定する際の処理プロセスについて、このローカル プロパティと継承プロパティの問題が存在します。
obj.propName=value のプロパティ プロパティを JavaScript コード内で設定できるかどうかを示すために、オブジェクトのプロパティ プロパティを記述します。 propName の属性が設定できない場合は、
2 が返されます。 obj.propName が存在しない場合、obj が作成するプロパティ、名前は propName
3. obj.propName の値は値
に設定されており、内部での設定プロセスは Prototype を考慮せず、道理は明らかであり、obj の [[Prototype]] は例示的なオブジェクトであり、obj に向けられたものではありません。
上の CF、Cfp の例を使用して説明すると、cf1.CFP1 を実行すると、オブジェクト cf1 は独自のプロパティ q1、q2 および継承プロパティ CFP1 を持ちます。 ="",那么cf1就有本地属性CFP1了,测试結果如下:




复制代
代码如下: //FF2.0、IE7、Opera9.25、Safari3.0.4 で渡されます var cf1=new CF("aaa", "bbb"); var cf2=新しい CF(111, 222);
document.write(cf1.CFP1 "
"); //結果: CFP1 の Cfp
document.write(cf2.CFP1 "
"); //結果: Cfp の CFP1
//結果は cf1 のローカル プロパティになります
cf1.CFP1="cf1 の新しい値";
//CF.prototype.CFP1 の変更は cf2 には影響しますが、cf1 には影響しません。これは、
//cf1 には CFP1 という名前のローカル プロパティがすでに存在しますが、cf2
CF.prototype にはそのようなプロパティは存在しないためです。 CFP1="Cfp の新しい値";
document.write(cf1.CFP1 "
"); //結果: cf1 の新しい値
document.write(cf2.CFP1 "
"); //結果: Cfp

の新しい値
意味上の混乱がありますか?
上記の CF、Cfp サンプル シナリオをまだ使用しています。
Prototype の仕組みによれば、オブジェクト cf1、cf2 などはすべてオブジェクト Cfp のプロパティやメソッドを継承していると言え、それらの間には継承関係があると言えます。属性の継承/共有は暗黙のプロトタイプ チェーンに沿って機能するため、継承関係もこのチェーンに沿ったものとして理解される必要があります。
もう一度、instanceOf 操作を見てみましょう。cf1 のinstanceOf CF のみが true であり、cf1 が Cfp のインスタンス オブジェクトであると言うのではなく、CF がクラスの役割を果たします。 cf1 は CF から継承すると言うべきでしょうか? しかし、CF はサードパーティ ファクトリとして機能するだけであり、cf1 との間に属性の継承関係はありません。
CF と Cfp を全体として理解するのも無理があります。

プロトタイプはプロトタイプです。JavaScript とオブジェクト指向の概念を無理に組み合わせる必要はありません。別の観点から見ると、JavaScript は関数型言語または動的言語とみなすことができます。複数の言語機能を組み込んだ合理化されたバージョンです。

オブジェクト モデル
ここはどこですか?
1. JavaScript のデータ型を理解し、Number のようなシステム組み込みオブジェクトには複数の ID があることを理解します。 a) それら自体は関数オブジェクトです。それらはエンジンによって内部的に実装されます。 b) それらはデータ型を表し、対応するデータ型を定義および操作するために使用できます。 c) それらの背後には、内部データ構造などのエンジンの内部実装メカニズムが隠されています。 JavaScript オブジェクトなどのコンストラクターになります。
2. プロトタイプのメカニズム、オブジェクトがそれらを介してプロパティとメソッドを継承する方法、およびオブジェクト作成プロセス中に JS エンジン内でプロトタイプ関係がどのように設定されるかを理解します。

ユーザー定義関数オブジェクト自体の作成プロセスを理解すると、JavaScript オブジェクト モデルの包括的な概要を把握できます。

Function オブジェクトの作成プロセス
JavaScript コードで関数を定義するとき、または Function を呼び出して関数を作成するとき、Function 関数は最終的に次のような形式で呼び出されます: var newFun=Function(funArgs,楽しいボディ);。関数オブジェクトを作成する主な手順は次のとおりです。
1. 組み込みオブジェクト fn を作成します
2. fn の内部 [[Prototype]] を Function.prototype に設定します
3.内部 [[ Call]] 属性。論理参照オブジェクト作成プロセスのステップ 3 を処理する内部実装メソッドです。
4. 内部 [[Construct]] 属性。論理参照を処理する内部実装メソッドです。オブジェクト作成プロセスのステップ 1、2、3、4
5. fn.length を funArgs.length に設定します。関数にパラメータがない場合は、fn.length を 0 に設定します。
6. new Object() Object オブジェクト fnProto
を作成します。 7. fnProto.constructor を fn
に設定します。 fn.prototype を fn
に返します。 ステップ 1 とステップ 6 の違いは次のとおりです。そのステップ 1 は、Object オブジェクトの実装に使用される内部データ構造 (組み込みオブジェクト構造) を作成し、必要な内部初期化作業を完了するだけですが、その [[Prototype]]、[[Call]]、[[Construct]]他の属性は null または内部初期化値である必要があります。つまり、オブジェクトを指していない ([[Prototype]] などのプロパティの場合)、または処理が含まれていない ([[Call などのプロパティの場合]) と理解できます。 ]]、[[Construct]] メソッド)。ステップ6では、前述のオブジェクト作成処理に従って新しいオブジェクトを作成し、その[[プロトタイプ]]などを設定します。
上記の処理手順からわかるように、関数を定義するときは常に、そのプロトタイプは Object インスタンスであるため、カスタム関数のインスタンス オブジェクトを作成するとき、デフォルトでは、そのプロトタイプ チェーンは Object.prototype を指します。
さらに、Function の特別な機能は、[[Call]] と [[Construct]] の処理ロジックが同じであることです。

JavaScript オブジェクト モデル

JavaScript オブジェクト モデルの実行モデル_javascript スキル赤い点線は、暗黙的なプロトタイプ チェーンを表します。
このオブジェクト モデル図には多くのものが含まれており、多くの箇所を注意深く理解する必要があるため、検証用のテスト コードを作成できます。この図を完全に理解すると、JavaScript 言語の理解もほぼ同じになります。以下に追加の手順を示します。
1. 図内には、組み込み関数コンストラクターが記載されている場所がいくつかあります。これは同じオブジェクトであり、テストおよび検証できます。

コードをコピーします コードは次のとおりです:
//FF2.0、IE7、Opera9.25、Safari3.0.4 で渡されます
Function==Function.constructor / /result: true
Function==Function.prototype.constructor //result: true
Function==Object.constructor //result: true
//関数もNumber.constructor、String.constructor、Array.constructor、RegExp.constructor などに相当します。
function fn(){}
Function==fn.constructor //result: true これはいくつかの問題を示しています。 関数ポイントシステムの組み込み関数コンストラクター (組み込み関数コンストラクター)。関数はブートストラップされます。システム内のすべての関数は関数から構築されます。


2. 左下隅の obj1、obj2...objn スコープは、次のようなコードで作成されたオブジェクトを参照します。
これらのオブジェクトは、ローカル コンストラクター メソッドがありますが、プロトタイプ チェーンから継承されたコンストラクター メソッド、つまり fn.prototype.constructor を取得します。関数オブジェクトの構築プロセスから、それが fn 自体であることがわかります。
右下隅の obj1、obj2...objn スコープは、次のようなコードで作成されたオブジェクトを参照します: var obj1=new Object(); または var obj1=new Number(123); ; または obj1=/w /; など。したがって、これらのオブジェクトのプロトタイプ チェーンのポイントと、プロトタイプ チェーンから継承されたコンストラクターの値 (コンストラクターが組み込みの Number コンストラクターであるか、組み込みの Object コンストラクターであるかなどを参照) は、特定のオブジェクトによって異なります。オブジェクトタイプ。また、var obj=new Object(123); この方法で作成されたオブジェクトのタイプは依然として Number であり、これもパラメータ値のタイプに基づいて決定する必要があることに注意してください。
同様に、ローカル コンストラクターはありませんが、プロトタイプ チェーンから継承されたコンストラクター メソッド、つまり組み込み *** コンストラクターを取得します。特定のコンストラクターはデータ型によって決まります。

3. 図のプロトタイプ チェーンに関する追加説明:
Object.prototype はチェーン全体の終点であり、その内部の [[Prototype]] は null です。
すべての関数のプロトタイプ チェーンは Function.prototype を指します。
関数のプロトタイプ チェーンは Function.prototype を指します。これは、設計者が関数をブートストラップされるように設計したため、仕様で必須です。 Function プロトタイプ チェーンがこの方法で設計された後は、Function.constructor==Function と Function instanceOf Function が両方とも true になります。さらに、Function はすでにトップレベルのコンストラクターですが、Function 自体も関数オブジェクトであり、何かによって作成される必要があるため、ブートストラップは意味的に合理的です。
Function.prototype のプロトタイプ チェーンは Object.prototype を指しますが、これも仕様で義務付けられています。まず、Function.prototype は Function のインスタンス オブジェクトです (typeof Function.prototype はそれが Function であることを知ることができますが、instanceOf は内部で追加で Prototype チェーンを設定しているためテストを通過できません)。そのため、Prototype の規則に従って、 Function.prototype の内部関数 [ [Prototype]] 値は Function.prototype オブジェクトである必要があります。つまり、その Prototype チェーンはそれ自体を指します。これにより、プロトタイプ チェーンに無限ループが作成される一方で、それ自体がエンドポイントになり、その結果、すべての関数オブジェクトが Object から派生しなくなります。この必須要件を追加すると、プロトタイプ チェーンのエンドポイントは 1 つだけになります。

4. Function.prototype は関数オブジェクトであるため、表示されるプロトタイプ属性、つまり Function.prototype.prototype を持つ必要がありますが、これは FireFox でのみアクセスでき、IE、Opera、またはサファリ。したがって、絵の中には存在しないことを示す記号が使われています。

5. デフォルトでは、ユーザー定義関数の [[Prototype]] 値は Object.prototype です。つまり、その暗黙のプロトタイプ チェーンは Object.prototype を指すため、図に示されています。ただし、これが常に当てはまるわけではありません。ユーザーがカスタム関数のプロトタイプ属性を設定する場合は状況が異なります。

実行モデル
実行コンテキストの概要
実行コンテキストは、JavaScript コードが実行される場所に存在し、JavaScript の実行範囲と期間処理などを完了するために使用される概念とメカニズムです。実行コンテキストには、変数オブジェクト、変数の初期化、スコープ/スコープ チェーンなどの概念が含まれます。異なるシナリオ/実行環境での処理にはいくつかの違いがあります。これらのシナリオについては、以下で説明します。

関数オブジェクトは、ユーザー定義関数オブジェクトとシステム組み込み関数オブジェクトに分けられ、ユーザー定義関数オブジェクトは以下の仕組みに従って処理されますが、組み込み関数オブジェクトは特定の実装に関連します。コンテキストの処理に関する要件はありません。つまり、コンテキストは一般的にこのセクションで説明する内容には適していません。

実行される JavaScript コードは 3 つのタイプに分けられ、これら 3 つのタイプの処理の違いについては後で説明します。
1. グローバル コード、つまり、どの関数にも含まれていないグローバル コード。 js ファイル、HTML ページに埋め込まれた js コードなど。
2. Eval コード。eval() 関数を使用して動的に実行される JS コードです。
3. 関数コード。ユーザー定義関数内の関数本体の JS コードです。

基本原則
ユーザー定義関数では、関数内でパラメーターを渡し、ローカル変数を定義できます。関数本体のコードでは、これらのパラメーターとローカル変数を使用できます。その背後にあるメカニズムは何ですか?
JS 実行フローが関数に入ると、JavaScript エンジンは変数オブジェクトと呼ばれるオブジェクトを内部的に作成します。関数の各パラメータに対応して、変数オブジェクトに属性を追加します。属性の名前と値は、パラメータの名前と値と同じです。関数内で変数が宣言されるたびに、その名前が変数オブジェクトに追加されます。そのため、変数に値を割り当てることは、変数オブジェクトの対応するプロパティに値を割り当てることを意味します。関数内のパラメーターまたはローカル変数にアクセスすると、変数 Object で対応するプロパティが検索され、その値が返されます。
一般に、変数オブジェクトは内部オブジェクトであり、JS コードで直接アクセスすることはできません。仕様では実装が必須ではないため、エンジン内の単なるデータ構造である可能性があります。

大まかに説明すると、スコープの概念は単純ではありません。たとえば、関数本体でグローバル変数を使用したり、関数をネストして定義したりすると、状況はさらに複雑になります。 。こうした状況にどう対処すればよいでしょうか? JavaScript エンジンは、ルールに従ってさまざまな実行位置に変数オブジェクトのリンク リストを作成します。変数にアクセスするときは、まずリンク リスト内の最初の変数オブジェクトを検索します。見つからない場合は、2 番目の変数の検索を続けます。検索が完了するまでオブジェクトを保持します。これがスコープ/スコープチェーンの一般的な概念です。

以下は各側面の詳細な処理です。

グローバル オブジェクト
JavaScript 実行環境には、HTML のウィンドウ オブジェクトなど、一意のグローバル オブジェクトであるグローバル オブジェクトが必要です。グローバル オブジェクトはホスト オブジェクトであり、JavaScript ランタイムのグローバル コンテナとしての役割に加えて、ECMA 仕様には追加の要件はありません。これには、Math、String、Date、parseInt および JavaScript のその他の組み込みグローバル オブジェクトと関数 (すべてグローバル オブジェクトの属性として) が含まれており、他のホスト環境で必要ないくつかの属性を含めることもできます。

変数オブジェクト
変数オブジェクトの基本概念は上で簡単に説明しました。変数オブジェクトを作成し、パラメーターとローカル変数を変数オブジェクトのプロパティに設定するプロセスは、変数の初期化と呼ばれます。これについては、スコープ チェーンと併せて後で詳しく説明します。

グローバル コード
変数オブジェクトはグローバル オブジェクトです。これが変数オブジェクトの唯一の特別な点です (アクセスできない内部オブジェクトであるという事実を指します)。
var globalVariable = "WWW";
document.write(window.globalVariable); //結果: WWW 上記のコードは、変数 globalVariable が定義されている場合、グローバル コード モードで実行されます。 、この属性をグローバル オブジェクト (つまり、ウィンドウ) オブジェクトに追加するため、出力は値 WWW になります。

ファンクションコード
変数オブジェクトはアクティベーションオブジェクトとも呼ばれます (いくつかの違いがあるため、違いを示すために仕様では新しい名前が付けられています。グローバルコード/評価コードでは変数オブジェクトと呼ばれます) 、関数コードでは変数オブジェクトと呼ばれます)。
関数の実行に入るたびに、新しいアクティベーション オブジェクト オブジェクトが作成され、次に引数オブジェクトが作成されてアクティベーション オブジェクトのプロパティとして設定され、変数のインスタンス化が処理されます。
関数を終了すると、アクティベーション オブジェクトは破棄されます (メモリ解放ではありませんが、ガベージ コレクションされる可能性があります)。

引数オブジェクトの属性:
長さ: 渡されるパラメータの実際の数です。関数オブジェクトの作成プロセスを参照すると、関数オブジェクトの長さは関数の定義時に必要なパラメーターの数です。
callee: は実行される関数オブジェクト自体です。その目的は、たとえば再帰呼び出しが必要な場合に、関数オブジェクトが自身を参照できるようにすることです。
function fnName(...) { ... } はこの方法で関数を定義し、その再帰呼び出しは関数本体で fnName を使用して完了できます。 var fn=function(...) { ... } この方法で匿名関数を定義します。関数本体で名前を使用して自分自身を参照することはできません。再帰呼び出しを実装するには、arguments.callee を通じて自分自身を参照できます。
パラメータ リスト: 呼び出し元によって実際に渡されるパラメータ リスト。このパラメータ リストは、インデックスを使用して実際のパラメータにアクセスする方法を提供します。関数の宣言時に指定されたパラメーター リストがある場合、変数のインスタンス化では、処理中にプロパティがアクティベーション オブジェクト オブジェクトに追加されます。関数宣言でパラメーター リストが指定されていない場合、または実際のパラメーターの数が関数宣言のパラメーターの数と異なる場合は、引数を介して各パラメーターにアクセスできます。

引数のパラメータ リストとアクティベーション オブジェクトのパラメータ属性は、同じパラメータ オブジェクトを参照します (変更された場合、両方の場所に反映されます)。仕様では、引数が配列オブジェクトである必要はありません。次にテストを示します。
コードをコピーします。 コードは次のとおりです。 🎜>
//FF2.0、IE7、Opera9.25、Safari3.0.4 で渡されます
var argumentLike = { 0: "aaa", 1: 222, 2: "WWW", length: 3、呼び出し先: function () { } };
document.write(argumentsLike[2] "
"); //結果: WWW
document.write(argumentsLike[1] "<) ;br /> "); //結果: 222
//引数プロパティの場合と同様に、argumentsLike を Array オブジェクトに変換します。
var array = [].slice.apply(argumentsLike );
document.write(array instanceof Array); //結果: true
document.write("
"); ("|") ); //結果: WWW|222|aaa
Eval コード
変数オブジェクトは、eval が呼び出されたときの現在の実行コンテキストの変数オブジェクトです。 eval 関数がグローバル コードで呼び出される場合、その変数オブジェクトはグローバル オブジェクトとなり、関数内で eval が呼び出される場合、その変数オブジェクトは関数のアクティブ化オブジェクトになります。
//FF2.0、IE7、Opera9.25、Safari3.0.4で渡されます
function fn(arg){
var innerVar = "関数内の変数"
eval('
var evalVar = "eval の変数";
document.write(arg "
");
document.write(innerVar "
"); ') ;
document.write(evalVar)
}
fn("関数の引数");
出力結果は次のとおりです。
関数の引数
関数の変数
eval の変数
説明: 関数 fn のパラメーターとローカル変数は、定義された eval ローカル変数でアクセスできます。 in eval 変数オブジェクトは同じオブジェクトであるため、関数 fn からもアクセスできます。

スコープ/スコープチェーン
まず、スコープチェーンは連結リスト/スタックに似た構造であり、その中の各要素は基本的に変数オブジェクト/アクティブ化オブジェクトです。
第二に、実行コンテキストがあるところには必ず現在のスコープ チェーンが存在します。スコープ チェーンは実行コンテキストの特定の表現であると理解できます。

グローバル コード
スコープ チェーンには、グローバル オブジェクトというオブジェクトが 1 つだけ含まれています。 JavaScript コードの実行を開始する前に、エンジンはこのスコープ チェーン構造を作成します。

関数コード
関数オブジェクトには内部 [[Scope]] 属性があり、関数が配置されているスコープ チェーンを記録するために使用されます。
関数オブジェクトを作成するとき、エンジンは現在の実行環境のスコープ チェーンを関数の [[Construct]] メソッドに渡します。 [[Construct]] は、渡されたスコープ チェーンと同じ内容を持つ新しいスコープ チェーンを作成し、それを作成された関数の内部 [[Scope]] 属性に割り当てます。関数オブジェクトの作成プロセスに関する前のセクションでは、このプロセスはステップ 4 と 5 の間にありました。
関数呼び出しを開始すると、同じ関数の再帰呼び出しを含む新しいスコープ チェーンも作成され、関数を終了するときにこのスコープ チェーンは破棄されます。新しく作成されたスコープ チェーンの最初のオブジェクトはアクティベーション オブジェクトで、次の内容は内部の [[Scope]] に格納されているスコープ チェーンの内容とまったく同じです。

評価コード
実行のために評価コードを入力すると、新しいスコープ チェーンが作成されます。その内容は、現在の実行コンテキストのスコープ チェーンとまったく同じです。

例の説明
スコープ チェーンの原理は上記のとおりです。上記の動作を理解するには、JS コードの実行と変数のインスタンス化の詳細を組み合わせる必要があります。以下で包括的に説明します。以下が JavaScript グローバル コードの一部であると仮定します:
コードをコピーします コードは次のとおりです:

var innerVar1= "グローバル コードの変数";
function fn1(arg1, arg2){
var innerVar1="関数コードの変数";
function fn2() { return innerVar1 " - " innerVar1 " - " " - " (arg1 arg2); }
return fn2();
}
var externalVar2=fn1(10, 20);

実行プロセスは大まかに次のとおりです。
1. ウィンドウ オブジェクトであるグローバル オブジェクトとウィンドウ オブジェクト自体である変数オブジェクトを初期化します。ウィンドウ オブジェクトのみを含むスコープ チェーン オブジェクトをscope_1 であると仮定して、スコープ チェーン オブジェクトを作成します。
2. JS ソース コードをスキャンします (ソース コードを読みます。字句解析および構文解析のプロセスがある場合があります)。その結果から、定義された変数名と関数オブジェクトを取得できます。スキャン順序に従って:
2.1 変数 externalVar1 を検出し、outerVar1 属性をウィンドウ オブジェクトに追加します。値は未定義です。
2.2 関数 fn1 の定義を検出し、この定義を使用して関数オブジェクトを作成します。作成プロセスに渡されるスコープ チェーンはscope_1です。結果を window プロパティに追加します。名前は fn1 で、値は返された関数オブジェクトです。 fn1 の内部 [[Scope]] はscope_1 であることに注意してください。なお、作成処理では関数本体内のJSコードに対して特別な処理は行わず、関数本体内のJSコードのスキャン結果を関数オブジェクトの内部プロパティに保存するだけであることが分かります。関数の実行時に処理されます。これは、関数コード、特に入れ子関数定義における変数のインスタンス化を理解するための鍵です。
2.3 変数 externalVar2 を検出し、outerVar2 属性をウィンドウ オブジェクトに追加します。値は未定義です。
3.そして値を「グローバルコード内の変数」に割り当てます。
4. 関数 fn1 を実行し、戻り値を取得します。
4.1 アクティベーション オブジェクトを作成します (有効化_1 であると仮定します)。新しいスコープ チェーンを作成します (有効範囲_2 の最初のオブジェクトが有効化_1 であると仮定します)。 object はウィンドウ オブジェクトです (fn1 の [[Scope]]、つまり、scope_1 の内容から取得されます)。
4.2 プロセス パラメーター リスト。 activity_1 の属性 arg1 と arg2 にそれぞれ値 10 と 20 を設定します。引数オブジェクトを作成して設定し、引数を activity_1 の属性として設定します。
4.3 fn1 の関数本体に対して手順 2 と同様のプロセスを実行します。
4.3.1 変数 innerVar1 を検出し、innerVar1 属性を追加します。 activity_1 オブジェクト、値は undefine;
4.3.2 関数 fn2 の定義を検出し、この定義を使用して関数オブジェクトを作成し、作成プロセスに渡されるスコープ チェーンはscope_2 です (関数 fn1 のスコープ チェーンは現在の実行コンテキストの内容)。結果を、fn2 という名前と返された関数オブジェクトの値を使用して、activation_1 の属性に追加します。 fn2 の内部 [[スコープ]] はscope_2 であることに注意してください。
4.4 innerVar1 代入ステートメントを実行し、値を「関数コード内の変数」に代入します。
4.5 fn2 を実行します。
4.5.1 アクティベーション オブジェクトを作成します (activation_2 であると仮定します); 新しいスコープ チェーンを作成します (scope_3 であると仮定します)。 . オブジェクト (fn2 の [[Scope]]、つまりscope_2 から取得);
4.5.2 プロセスパラメータリスト。 fn2 にはパラメーターがないため、引数オブジェクトを作成し、それを activity_2 の属性として設定するだけです。
4.5.3 fn2の関数本体に対して手順2と同様の処理を行うと、変数定義や関数宣言が見つかりません。
4.5.4 関数本体を実行します。変数参照の場合、scope_3 から検索します。この例では、outerVar1 は window で見つかり、innerVar1、arg1、および arg2 は activity_1 で見つかります。
4.5.5 スコープ_3 とアクティベーション_2 を破棄します (ガベージ コレクションできることを意味します)。
4.5.6 fn2の戻り値を返します。
4.6 activity_1 とscope_2 を破棄します。
4.7 結果を返します。
5. 結果をouterVar2に代入します。

他の場合でも、スコープチェーンと変数のインスタンス化は、上記のプロセスと同様に分析できます。

上記の例によれば、次のテスト コードの結果を説明できます。
コードをコピーしますコードは次のとおりです:

//FF2.0、IE7、Opera9.25、Safari3.0.4 で渡されます
function fn(obj){
return {
/ /obj にローカル変数 "outerVar" が存在するかどうかをテストします
exists: Object.prototype.hasOwnProperty.call(obj, "outerVar"),
//変数 "outerVar" の値をテストします
value : obj.outerVar
};
var result1 = fn(ウィンドウ);
var result2 = fn(ウィンドウ); >document.write( result1.exists " " result1.value); //result: true 未定義
document.write("
");
document.write(result2.exists " " result2.value); //result: true WWWresult1 が呼び出された場合、outerVar 宣言と代入ステートメントは実行されていませんが、テスト結果ウィンドウ オブジェクトにはすでにローカル プロパティ externalVar があり、その値は未定義です。 result2 の externalVar には値が割り当てられているため、window.outerVar の値はすでに存在します。実際に使用する場合は、使用してから定義しないと、仕様書に記載されていないメーカーごとの実装方法に矛盾が生じるため、問題が発生する場合があります。


특수 처리
1.(obj) { ... }를 사용하여 이 구문을 구현하는 것은 obj 객체를 현재 범위 체인 앞에 삽입하여 obj가 먼저 검색되는지 확인하는 것입니다. 해당 이름을 가진 속성이 존재합니다. 다른 유사한 것에는 catch 문이 포함됩니다.
2. 이전의 인수 개체에 대한 자세한 설명에서 재귀 함수 호출 지원에 대해 언급했습니다. 위의 내용은 스코프 체인의 작동 원리로 인해 아직 이 현상을 설명할 수 없습니다. 여기에는 특별한 처리가 있기 때문입니다.
이름이 지정된 함수 개체가 생성될 때마다 JavaScript 엔진은 현재 실행 컨텍스트 범위 체인 앞에 개체를 삽입합니다. 이 개체는 새 Object() 메서드를 사용하여 생성되고 범위 체인이 전달됩니다. 함수 생성자 [ [Construct]], 최종 생성된 함수 개체는 [[Scope]] 내부에 이 개체 개체를 포함합니다. 생성 프로세스가 반환된 후 JavaScript 엔진은 함수 이름과 반환된 함수 개체의 값을 사용하여 개체에 속성을 추가한 다음 현재 실행 컨텍스트의 범위 체인에서 이를 제거합니다. 이런 식으로 함수 객체의 범위 체인의 첫 번째 객체는 자신에 대한 참조이며 제거 작업은 함수 객체가 생성된 범위 체인의 복원을 보장합니다.

이 키워드 처리
실행 컨텍스트에 포함된 또 다른 개념은 this 키워드입니다.
글로벌 코드의 this 키워드는 전역 개체입니다. 함수가 호출될 때 this 키워드는 호출자입니다(예: obj1.fn1()). fn1에서 this 개체는 평가 코드의 this 키워드입니다. 현재 실행 컨텍스트 개체의 변수입니다.

함수를 호출할 때 JavaScript는 사용자가 이 키워드의 값, 즉 각 함수가 갖는 호출 및 적용 메서드를 지정할 수 있는 기회를 제공합니다. 예:
fn1.call(obj1, arg1, arg2, ...) 또는 fn1.apply(obj1, argArray)는 모두 obj1을 이 키워드로 사용하여 fn1 함수를 호출하고 실행하며 모든 후속 매개변수는 다음과 같이 사용됩니다. 함수 fn1 매개변수. obj1이 null이거나 정의되지 않은 경우 전역 개체가 이 키워드의 값으로 사용되며, obj1이 개체 유형이 아닌 경우 개체 유형으로 변환됩니다. 유일한 차이점은 Apply를 사용하면 각 매개변수를 배열 형식으로 제공할 수 있는 반면, 호출 메소드는 매개변수를 하나씩 제공해야 한다는 것입니다.
이 방법은 이전 테스트 샘플 코드의 여러 곳에서 사용되었습니다. 예를 들어 window 객체에는 hasOwnProperty 메서드가 없습니다. 또한 Object.prototype.hasOwnProperty.call(window, "propertyName")을 사용하여 로컬 속성이 있는지 테스트할 수도 있습니다.

JavaScript의 클로저
예:
코드 복사 코드는 다음과 같습니다.

//FF2.0, IE7, Opera9.25, Safari3.0.4에서 전달됨
function external(){
var a="aaa"
var b="bbb";
return function(){ return a " " b;
}
var inner=outer();
document.write(inner());outer는 인라인 함수를 반환합니다. 함수는 외부의 지역 변수 a와 b를 사용합니다. 논리적으로 말하면, 외부의 지역 변수는 반환될 때 범위를 벗어나므로 inner() 호출을 사용할 수 없습니다. 이것이 클로저입니다. 즉, 함수 호출이 포함된 함수를 반환하고, 포함된 함수는 닫혀야 하는 외부 함수의 지역 변수, 매개변수 및 기타 리소스를 참조합니다(Close).

스코프 체인에 대한 이전 이해에 따르면 반환된 인라인 함수는 생성 당시 이미 스코프 체인을 보유하고 있다고 설명할 수 있습니다. 비록 외부 반환으로 인해 이러한 개체가 범위 및 수명 범위를 초과하게 됩니다. , JavaScript는 자동 가비지 수집을 사용하여 객체 메모리를 확보합니다. 규칙에 따라 주기적으로 확인되며 참조가 없는 경우에만 객체가 해제됩니다. 따라서 위의 코드는 올바르게 실행됩니다.
関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート