ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript のスコープとスコープのchain_javascript スキルについての深い理解

JavaScript のスコープとスコープのchain_javascript スキルについての深い理解

WBOY
WBOYオリジナル
2016-05-16 18:00:43840ブラウズ

スコープは JavaScript の最も重要な概念の 1 つです。JavaScript をよく学びたい場合は、JavaScript のスコープとスコープ チェーンがどのように機能するかを理解する必要があります。今日の記事では、JavaScript のスコープとスコープ チェーンについて簡単に紹介し、誰もが JavaScript をより良く学習できるようにしたいと考えています。

JavaScript スコープ

どのプログラミング言語にもスコープの概念があります。簡単に言うと、スコープは変数と関数のアクセス可能な範囲です。変数と関数のライフサイクル。 JavaScript では、変数のスコープにはグローバル スコープとローカル スコープの 2 種類があります。

1. グローバル スコープ

コード内のどこからでもアクセスできるオブジェクトにはグローバル スコープがあります。

(1) 最も外側のオブジェクト。最も外側の関数の外側で定義された関数と変数にはグローバル スコープがあります。例:

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

var authorName="マウンテンサイド ストリーム";
function doSomething(){
var blogName="Dream Sky";
function innerSay(){
alert(ブログ名); 🎜>}
innerSay();
}
alert(authorName); //マウンテンサイド クリーク
alert(ブログ名); //スクリプト エラー
doSomething(); 🎜>innerSay() //スクリプト エラー


(2) すべての未定義変数および直接割り当てられた変数は、グローバル スコープを持つように自動的に宣言されます。例:


コードをコピーします コードは次のとおりです。
function doSomething(){
var authorName="Mountainside Stream";
blogName= "Dream Sky";
alert(authorName);
alert(blogName); //Dream Sky

変数 blogName にはグローバル スコープがあり、関数の外部から authorName にアクセスすることはできません。

(3) window オブジェクトのすべてのプロパティはグローバル スコープを持ちます。
通常、window オブジェクトの組み込みプロパティは、window.name、window.location、window などのグローバル スコープを持ちます。 .topなど。

1. ローカル スコープ

グローバル スコープとは対照的に、ローカル スコープは通常、関数内などの固定コード フラグメント内でのみアクセスできます。また、このスコープを関数スコープと呼ぶ人もいます。たとえば、次のコードの blogName と関数 innerSay はローカル スコープのみを持ちます。




コードをコピー
コードは次のとおりです。 function doSomething(){ var blogName=" Dream Sky"; function innerSay(){
alert(blogName);
innerSay(); //スクリプトエラー
innerSay(); //スクリプト エラー


スコープ チェーン

実際、JavaScript 内の関数はすべてオブジェクトです。関数オブジェクトには、他のオブジェクトと同様に、コードを通じてアクセスできるプロパティと、JavaScript エンジンのみがアクセスできる一連の内部プロパティがあります。内部プロパティの 1 つは [[Scope]] で、ECMA-262 標準の第 3 版で定義されています。この内部プロパティには、関数が作成されるスコープ内のオブジェクトのコレクションが含まれます。このコレクションは、スコープ チェーンと呼ばれます。関数によってアクセスできるデータを決定する関数。

関数が作成されると、そのスコープ チェーンには、関数が作成されたスコープからアクセスできるデータ オブジェクトが設定されます。たとえば、次のような関数を定義します。



コードをコピー


コードは次のとおりです。
function add(num1, num2) { var sum = num1 return sum; }
関数 add が作成されると、グローバル オブジェクトが埋められます。グローバル オブジェクトには、そのスコープ チェーン内に、以下の図に示すように、すべてのグローバル変数が含まれています (注: この図は、すべての変数の一部のみを示しています):



関数 add のスコープは実行時に使用されます。たとえば、次のコードを実行します。



コード
JavaScript作用域链
をコピーします。 コードは次のとおりです。
var total = add(5, 10);この関数が実行されると、「実行コンテキスト」と呼ばれる内部オブジェクトが作成され、関数が実行される環境が定義されます。各ランタイム コンテキストには、識別子解決のための独自のスコープ チェーンがあり、ランタイム コンテキストが作成されると、そのスコープ チェーンは、現在実行中の関数の [[Scope]] に含まれるオブジェクトに初期化されます。

値は、関数内に出現する順序でランタイム コンテキストのスコープ チェーンにコピーされます。これらは一緒に「アクティベーション オブジェクト」と呼ばれる新しいオブジェクトを形成します。このオブジェクトには、すべてのローカル変数、名前付きパラメータ、パラメータ コレクションが含まれており、実行時にスコープ チェーンのフロント エンドにプッシュされます。コンテキストが破棄されると、アクティブなオブジェクトも破棄されます。新しいスコープ チェーンを以下に示します:
JavaScript作用域链
関数の実行中に変数が見つからない場合、識別子の解析プロセスを経て、データを取得して保存する場所が決定されます。このプロセスはスコープ チェーンの先頭、つまりアクティブなオブジェクトから開始され、同じ名前の識別子が見つかった場合は、その識別子に対応する変数を使用します。見つからない場合は、続行します。スコープチェーン内の次のオブジェクトを検索します。 If 検索後にオブジェクトが見つからない場合、識別子は未定義とみなされます。関数実行時には、各識別子に対してこのような検索処理が行われます。

スコープ チェーンとコードの最適化

スコープ チェーンの構造から、実行時コンテキストのスコープ チェーン内での識別子の位置は深くなるほど読み書き速度が遅くなります。上の図に示すように、グローバル変数は常にランタイム コンテキスト スコープ チェーンの最後に存在するため、識別子の解決中にグローバル変数を見つけるのが最も遅くなります。したがって、コードを記述するときは、グローバル変数の使用を最小限に抑え、可能な限りローカル変数を使用する必要があります。経験則としては、クロススコープ オブジェクトが複数回参照される場合は、使用する前にローカル変数に格納します。たとえば、次のコード:
コードをコピー コードは次のとおりです:

function changeColor (){
document.getElementById("btnChange").onclick=function(){
document.getElementById("targetCanvas").style.backgroundColor="red"};


この関数は、グローバル変数ドキュメントを 2 回参照し、変数を見つけるには、グローバル オブジェクトで最終的に見つかるまでスコープ チェーン全体をたどる必要があります。このコードは次のように書き換えることができます:


function changeColor() {
var doc=document;
doc.getElementById("btnChange").onclick=function(){
doc.getElementById("targetCanvas").style.backgroundColor="red";
};
}


このコードは比較的単純であり、プログラム内に多数のグローバル変数がある場合、書き換え後のパフォーマンスの大幅な向上は見られません。繰り返しアクセスして書き直すと、結果として得られるコードのパフォーマンスが大幅に向上します。


スコープ チェーンを変更する
対応するランタイム コンテキストは関数が実行されるたびに一意であるため、同じ関数を複数回呼び出すと複数のランタイムが作成されます。コンテキストでは、関数の実行が完了すると、実行コンテキストは破棄されます。各ランタイム コンテキストはスコープ チェーンに関連付けられます。通常、ランタイム コンテキストの実行中、そのスコープ チェーンは with ステートメントと catch ステートメントによってのみ影響を受けます。

with ステートメントは、オブジェクトを使用してコードを繰り返し記述することを避けるためのショートカット方法です。例:


function initUI(){
with( document){
var bd=body,
links=getElementsByTagName("a"),
i=0,
len=links.length>while(i < ; len){
update(links[i ]);
getElementById("btnInit").onclick=function(){
doSomething();
}
}


ここで width ステートメントは、ドキュメントを複数回記述することを避けるために使用されており、効率的であるように見えますが、実際にはパフォーマンスの問題が発生します。

コードが with ステートメントまで実行されると、ランタイム コンテキストのスコープ チェーンが一時的に変更されます。パラメーターで指定されたオブジェクトのすべてのプロパティを含む新しい可変オブジェクトが作成されます。このオブジェクトはスコープ チェーンの先頭にプッシュされます。つまり、関数のすべてのローカル変数が 2 番目のスコープ チェーン オブジェクト内にあるため、アクセスコストが高くなります。以下に示すように:
JavaScript作用域链
したがって、この例では、ドキュメントをローカル変数に保存するだけでパフォーマンスが向上するため、プログラム内で with ステートメントを使用することは避けてください。

スコープ チェーンを変更するもう 1 つの点は、try-catch ステートメント内の catch ステートメントです。 try コード ブロックでエラーが発生すると、実行プロセスは catch ステートメントにジャンプし、その後、例外オブジェクトが可変オブジェクトにプッシュされ、スコープの先頭に配置されます。 catch ブロック内では、関数のすべてのローカル変数が 2 番目のスコープ チェーン オブジェクトに配置されます。サンプルコード:
コードをコピー コードは次のとおりです:

try{
doSomething ();
}catch(ex){
alert(ex.message); //ここでスコープチェーンが変更されます
}

catch ステートメントが実行すると、スコープ チェーンは以前の状態に戻ります。 try-catch ステートメントはコードのデバッグや例外処理に非常に役立つため、完全に回避することはお勧めできません。コードを最適化することで、catch ステートメントのパフォーマンスへの影響を軽減できます。良いパターンは、エラー処理を関数に委任することです。例:
コードをコピー コードは次のとおりです:

try{
doSomething();
}catch(ex){
handleError(ex); //プロセッサメソッドに委譲します

最適化されたコードでは、catch 句で実行されるコードは handleError メソッドだけです。この関数は例外オブジェクトをパラメータとして受け取るため、エラーをより柔軟かつ均一に処理できます。実行されるステートメントは 1 つだけであり、ローカル変数にはアクセスされないため、スコープ チェーンの一時的な変更はコードのパフォーマンスに影響しません。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。