ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript スコープ チェーン パート 2: 関数のライフ サイクル

JavaScript スコープ チェーン パート 2: 関数のライフ サイクル

黄舟
黄舟オリジナル
2016-12-20 16:21:281294ブラウズ

関数のライフサイクルは、作成フェーズとアクティブ化フェーズ (呼び出されたとき) に分かれています。詳しく調べてみましょう。

関数の作成

ご存知のとおり、関数宣言はコンテキストに入るときに変数/アクティビティ (VO/AO) オブジェクトに入れられます。グローバル コンテキストで変数と関数の宣言を見てみましょう (ここで、変数オブジェクトはグローバル オブジェクトそのものです。覚えていますよね?)

var x = 10;
  
function foo() {
  var y = 20;
  alert(x + y);
}
  
foo(); // 30

関数をアクティブ化すると、正しい (期待される) 結果 - 30 が得られます。ただし、非常に重要な機能が 1 つあります。

以前は、現在のコンテキストに関連する変数オブジェクトについてのみ説明しました。ここでは、変数 "y" が関数 "foo" で定義されている (つまり、foo コンテキストの AO 内にある) ことがわかりますが、変数 "x" は "foo" コンテキストで定義されていないため、次のようになります。 「foo」の AO には追加されません。一見すると、変数「x」は関数「foo」に対してまったく存在しませんが、以下に示すように、「一見」しただけでは、「foo」コンテキストのアクティブなオブジェクトには次のものが含まれているだけであることがわかります。 1 つの属性 - 「y」。

fooContext.AO = {
  y: undefined // undefined – 进入上下文的时候是20 – at activation
};

関数「foo」は変数「x」にどのようにアクセスしますか?理論的には、関数は上位レベルのコンテキストの変数オブジェクトにアクセスできる必要があります。実際、このメカニズムは関数内の [[scope]] 属性によって実装されます。

[[scope]] は、すべての親変数オブジェクトの階層チェーンであり、現在の関数コンテキストの上にあり、関数の作成時に保存されます。

この重要な点に注意してください - [[scope]] は関数の作成時に保存され、関数が破棄されるまで静的 (不変) に永久に保存されます。つまり、関数を呼び出すことはできませんが、[[scope]] 属性が関数オブジェクトに書き込まれ、保存されています。

もう 1 つ考慮すべき点は、スコープ チェーンと比較すると、[[scope]] はコンテキストではなく関数の属性であるということです。上記の例を考慮すると、関数 "foo" の [[scope]] は次のようになります:

foo.[[Scope]] = [
  globalContext.VO // === Global
];

たとえば、スコープと [[scope]] を表すために通常の ECMAScript 配列を使用します。

続けて、関数が呼び出されたときにコンテキストが入ることがわかります。このとき、アクティブなオブジェクトが作成され、これとスコープ(スコープチェーン)が決定されます。この瞬間を詳しく考えてみましょう。

関数の起動

定義でも述べたように、AO/VOを作成するためにコンテキストに入った後、コンテキストのScope属性(変数検索用のスコープチェーン)は次のように定義されます:

Scope = AO|VO + [[Scope]]

上記コードの意味is: active object スコープ配列内の最初のオブジェクト、つまりスコープに追加されたフロントエンドです。

Scope = [AO].concat([[Scope]]);

この機能は、識別子の解析処理にとって非常に重要です。識別子の解決は、変数 (または関数宣言) がどの変数オブジェクトに属しているかを決定するプロセスです。

このアルゴリズムの戻り値には常に参照型があり、その基本コンポーネントは対応する変数オブジェクト (見つからない場合は null)、属性名のコンポーネントは検索された識別子の名前です。参照型の詳細については後で説明します。

識別子の解決プロセスには、変数名に対応する属性の検索が含まれます。つまり、スコープ内の変数オブジェクトを最も深いコンテキストから開始して最上位レベルまでのスコープチェーンをバイパスして継続的に検索します。

このように、上向き検索では、コンテキスト内のローカル変数が親スコープ内の変数よりも優先されます。 2 つの変数の名前は同じですが、スコープが異なる場合、最初に見つかった変数が最も深いスコープにあります。

上で述べたことを説明するために、もう少し複雑な例を使用します。

var x = 10;
  
function foo() {
  var y = 20;
  
  function bar() {
    var z = 30;
    alert(x +  y + z);
  }
  
  bar();
}
  
foo(); // 60

このために、次の変数/アクティビティ、関数の [[scope]] 属性、およびコンテキストのスコープ チェーンがあります:

グローバル コンテキストの変数オブジェクトは次のとおりです:

globalContext.VO === Global = {
  x: 10
  foo: <reference to function>
};

When "foo"が作成されます。「foo」の [[scope]] 属性は次のとおりです:

foo.[[Scope]] = [
  globalContext.VO
];

「foo」がアクティブ化される (コンテキストに入る) と、「foo」コンテキストのアクティブなオブジェクトは次のとおりです:

fooContext.AO = {
  y: 20,
  bar: <reference to function>
};

"foo" コンテキストは:

fooContext.Scope = fooContext.AO + foo.[[Scope]] // i.e.:
  
fooContext.Scope = [
  fooContext.AO,
  globalContext.VO
];

内部関数 "bar" が作成されるとき、その [[scope]] は:

bar.[[Scope]] = [
  fooContext.AO,
  globalContext.VO
];

"bar" がアクティブ化されるとき、"bar" コンテキストのアクティブ オブジェクトは:

barContext.AO = {
  z: 30
};

「bar」コンテキストのスコープ チェーンは次のとおりです:

barContext.Scope = barContext.AO + bar.[[Scope]] // i.e.:
  
barContext.Scope = [
  barContext.AO,
  fooContext.AO,
  globalContext.VO
];

「x」、「y」、および「z」の識別子は次のように分析されます:

- "x"
-- barContext.AO // not found
-- fooContext.AO // not found
-- globalContext.VO // found - 10
 
- "y"
-- barContext.AO // not found
-- fooContext.AO // found - 20
 
- "z"
-- barContext.AO // found - 30

上記は JavaScript スコープ チェーンの 2 番目の部分です。関数のライフサイクルの内容 さらに関連する内容については、PHP 中国語 Web サイト (www .php.cn) に注目してください。


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。