ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript の字句スコープと object_javascript スキルの呼び出しについての深い理解

JavaScript の字句スコープと object_javascript スキルの呼び出しについての深い理解

WBOY
WBOYオリジナル
2016-05-16 17:47:34970ブラウズ

JavaScript における関数スコープ、オブジェクトの呼び出し、およびクロージャの関係は非常に微妙です。それらについての記事はたくさんありますが、なぜ多くの初心者がそれらを理解するのが難しいのかわかりません。私自身の理解をより一般的な言語で表現してみます。
スコープ Scope
JavaScript の関数は字句スコープに属します。これは、関数が実行時ではなく定義時にスコープ内で実行されることを意味します。サイの本にはこう書いてあります。しかし、「定義時」と「実行(呼び出し)時」の2つを混同している人もいます。簡単に言うと、関数 A が「定義」されているときは、関数 A(){} です。ステートメントが実行されるときは、関数が定義されたときです。また、A が呼び出されるときは、ステートメント A( )が実行されます。これら 2 つの概念は明確に区別する必要があります。
レキシカルスコープ(以下、特に指定のない限り「スコープ」と呼びます)とは何ですか?抽象的な概念ですが、端的に言えば「スコープ」であり、スコープとは英語でスコープという意味です。関数のスコープは、関数が定義されたときの「スコープ」です。つまり、この「スコープ」には外部変数の属性が含まれます。この「スコープ」は関数の内部状態に設定されます。グローバル関数が定義されると、グローバル関数の「スコープ」(関数の外層) がグローバル関数の内部状態に設定されます。入れ子関数が定義されると、入れ子関数 (外部関数) の「スコープ」が入れ子関数の内部状態に設定されます。この「内部状態」は、実際にはスコープ チェーンとして理解できます (以下を参照)。
上記のステートメントによると、関数のスコープはそれが定義されている「スコープ」であり、JavaScript の関数スコープは関数の定義時に決定されるため、静的スコープとなります。静的スコープとも呼ばれます。
Call Object
関数の呼び出しオブジェクトは動的であり、関数が呼び出されるときにインスタンス化されます。関数が定義されると、そのスコープ チェーンが決定されることはすでにわかっています。 JavaScript インタープリターが関数を呼び出すと、新しいオブジェクト (呼び出し側オブジェクト) がスコープ チェーンの先頭に追加されます。呼び出し元オブジェクトのプロパティは、arguments と呼ばれるプロパティに初期化されます。このプロパティは、関数の実際のパラメータである関数の Arguments オブジェクトを参照します。 var ステートメントで宣言されたすべてのローカル変数も、この呼び出しオブジェクトで定義されます。現時点では、呼び出し元のオブジェクトはスコープ チェーンの先頭にあり、ローカル変数、関数の仮パラメータ、および Arguments オブジェクトはすべてこの関数のスコープ内にあります。もちろん、この時点では、ローカル変数、関数の仮パラメータ、および Arguments オブジェクトによって、スコープ チェーン内の同じ名前のプロパティが上書きされます。
スコープ、スコープチェーン、呼び出し元オブジェクトの関係
私の理解では、スコープは抽象的であり、呼び出し元のオブジェクトはインスタンス化されます。
関数が定義されるとき、つまりその外部関数が実行されるとき、その関数が決定するスコープ チェーンは、実際にはその関数が呼び出されるときのその外部関数の呼び出しオブジェクト チェーンであり、そのスコープ チェーンはスコープに基づきます。定義時に決定されたチェーン (その外部関数の呼び出しオブジェクト チェーン) に、インスタンス化された呼び出しオブジェクトを加えたもの。したがって、関数のスコープ チェーンは実際には呼び出し元のオブジェクト チェーンになります。関数が呼び出されるとき、そのスコープ チェーン (または呼び出しオブジェクト チェーン) は、実際には、定義時に決定されたスコープ チェーンのスーパーセットです。
それらの間の関係は次のように表現できます: スコープ? スコープ チェーン? 呼び出し元オブジェクト。
これは複雑すぎるため、例を挙げてみましょう:

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

function f(x) {
var g = function () { return x; }
return g;
}
var g1 =
alert( g1()) ; //出力 1
グローバル状況を次のような大きな匿名関数とみなします:
(function() {
//これはグローバル スコープです
} )();
この例は次のようになります:
(function() {
function f(x) {
var g = function () { return x; }
return g;
}
var g1 =
alert(g1()); // 出力 1


グローバルな大きな匿名関数が定義されている場合、この関数には外層がないため、そのスコープ チェーンは空です。
グローバルで大きな匿名関数は直接実行され、グローバル スコープ チェーンには「グローバル呼び出しオブジェクト」が 1 つだけ存在します。
関数 f が定義されているとき、関数 f のスコープ チェーンはその外側のスコープ チェーン、つまり「グローバル呼び出しオブジェクト」です。
関数 f(1) が実行され、そのスコープ チェーンは、新しい f(1) 呼び出しオブジェクトに関数 f が定義されたときのスコープ チェーンを加えたものになります。つまり、「f(1) 呼び出しオブジェクト -> グローバル呼び出しオブジェクト」 。
関数 g (g1 に返されるので、g1 と名付けます) は f(1) で定義されており、そのスコープ チェーンはその外側の関数 f(1) のスコープ チェーン、つまり ' f(1 ) 呼び出しオブジェクト -> グローバル呼び出しオブジェクト'。
関数 f(1) は関数 g の定義を g1 に返します。
関数 g1 が実行され、そのスコープ チェーンは、新しい g(1) 呼び出しオブジェクトと外側の f(1) のスコープ チェーンを加えたものになります。つまり、「g1 呼び出しオブジェクト - > f(1) 呼び出しオブジェクト -」 >「グローバル呼び出しオブジェクト」。
こうして見るとよくわかります。
Closuer
クロージャを簡単に言うと、入れ子になった関数が入れ子になった関数の外側で呼び出されるときにクロージャが形成されるということです。
前の例は実際にはクロージャです。 g1 は f(1) 内で定義されていますが、f(1) が戻った後に実行されます。クロージャの効果の 1 つは、入れ子関数 f が戻った後、その内部リソースが解放されないことです。 g 関数が外部から呼び出される場合、g は f の内部変数にアクセスできます。この機能に基づいて、多くのエレガントなコードを作成できます。
たとえば、ページ上に統合カウンタを作成したい場合、クロージャを使用すると、次のように記述できます:
コードをコピー コードは次のとおりです:

var counter = (function() {
var i = 0;
var fns = {"get": function( ) {return i;},
"inc": function() {return i;}};
//何かを行います
inc();
//別の処理を行います
counter.inc();
var c_value = counter.get(); // この場合、c_value は 2 になります。このように、1 つはメモリ変数 i に保持されますが、i の値はプログラム全体の他の場所で直接操作することはできず、カウンターの 2 つの操作を介してのみ操作できます。
setTimeout(fn, late) の間、関数ハンドル fn にパラメータを渡すことはできませんが、クロージャ メソッドを通じて必要なパラメータを fn の内部にバインドすることができます。



コードをコピー
コードは次のとおりです。 for(var i=0,lay= 1000; isetTimeout(function() { console.log('i:' i " 遅延:" 遅延);
}, 遅延); 🎜>}


このようにして、出力される値はすべて
i:5 late:6000
i:5 late:6000
i:5 late:6000
i:5 遅延:6000
i:5 遅延:6000
代わりにクロージャを使用すると、渡されるパラメータを簡単にバインドできます:




Copy code

コードは次のとおりです: for(var i=0, late=1000; i (function(a , _lay) { setTimeout(function() { console.log('i:' a " late:" _delivery);
}, _lay);
}) (i, 遅延) ;
}


出力:
i:0 遅延:1000
i:1 遅延:2000
i:2 遅延:3000
i:3 遅延 :4000
i:4 遅延:5000
クロージャは、イベント コールバック関数をバインドするときにもよく使用されます。同じ理由で、バインドされた関数ハンドルはパラメータとして使用できませんが、パラメータはクロージャの形式でバインドできます。

概要

関数の字句スコープとスコープ チェーンは別のものです。字句スコープは抽象概念であり、スコープ チェーンはインスタンス化された呼び出しオブジェクトのチェーンです。
関数が定義されると、その外側の関数が実行されることになります。
関数の語彙範囲は定義時に決定されますが、依然として抽象的な概念であり、インスタンス化することはできません。
関数が定義されると、インスタンス化される外部関数のスコープ チェーンという 1 つのことも決まります。 関数が複数回呼び出される場合、そのスコープ チェーンは異なります。 クロージャは強力です。 Rhinoceros の本は正しく、これらのことを理解できれば、上級 Javascript プログラマーと呼ぶことができます。これらの概念をうまく活用することで、JavaScript のさまざまなデザイン パターンを試すことができるからです。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。