ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript スコープとスコープchain_javascript スキルの詳細な分析

JavaScript スコープとスコープchain_javascript スキルの詳細な分析

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

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

JavaScript スコープ

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

1. グローバル スコープ

コード内のどこからでもアクセスできるオブジェクトにはグローバル スコープがあります。一般に、グローバル スコープを持つ状況がいくつかあります。

(1) 最も外側の関数とその外側で定義された変数はグローバル スコープを持ちます。例:


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


(2) 定義されておらず、値が直接割り当てられていないすべての変数は、次のように自動的にグローバル スコープを持つように宣言されます。


コードをコピーします
コードは次のとおりです:function doSomething(){ var authorName=" Shanbian Xiao Stream"; blogName="";
alert(authorName);
}
alert(blogName); //
alert(authorName); // スクリプト エラー


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

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

2. ローカルスコープ

グローバル スコープとは対照的に、ローカル スコープは通常、固定されたコード フラグメント内でのみアクセスできます。最も一般的なものは関数内にあるため、場所によっては、このスコープを関数スコープと呼んでいる場合があります。次のコードの、blogName および関数 innerSay はローカル スコープのみを持ちます。

コードをコピー

コードは次のとおりです。function doSomething(){ var blogName=" "; 関数 innerSay(){
alert(ブログ名)
}
alert(ブログ名); >innerSay( ); //スクリプト エラー


スコープ チェーン

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

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


コードをコピーします

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

function add(num1, num2) { var sum = num1 num2 } 関数 add が作成されると、グローバル オブジェクトが入力されます。次の図に示すように、グローバル オブジェクトにはすべてのグローバル変数が含まれています (注: この図はすべての変数の一部のみを示しています):




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



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


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

値は、関数内に出現する順序でランタイム コンテキストのスコープ チェーンにコピーされます。これらは一緒に「アクティベーション オブジェクト」と呼ばれる新しいオブジェクトを形成します。このオブジェクトには、すべてのローカル変数、名前付きパラメータ、パラメータ コレクションが含まれており、実行時にこのオブジェクトがスコープ チェーンのフロント エンドにプッシュされます。コンテキストが破棄されると、アクティブなオブジェクトも破棄されます。新しいスコープ チェーンは次のとおりです:

関数の実行中に変数が見つからない場合、識別子の解析プロセスを経て、データを取得して保存する場所が決定されます。このプロセスはスコープ チェーンの先頭、つまりアクティブなオブジェクトから開始され、同じ名前の識別子が見つかった場合は、その識別子に対応する変数を使用します。見つからない場合は、続行します。スコープチェーン内の次のオブジェクトを検索します。 If 検索後にオブジェクトが見つからない場合、識別子は未定義とみなされます。関数実行時には、各識別子に対してこのような検索処理が行われます。

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

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

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

function changeColor (){

document.getElementById("btnChange").onclick=function(){

document.getElementById("targetCanvas").style.backgroundColor="red";

};

}


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

関数changeColor() {

var doc=ドキュメント

doc.getElementById("btnChange").onclick=function(){

doc.getElementById("targetCanvas").style.backgroundColor="red";

};

}


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

スコープチェーンの変更

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

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

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

function initUI(){

with(ドキュメント){

var bd=body,

links=getElementsByTagName("a"),

i=0、

len=リンクの長さ

while(i

更新(リンク[i ]);

}

getElementById("btnInit").onclick=function(){

doSomething()

};

}

}



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

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


したがって、この例では、ドキュメントをローカル変数に保存するだけでパフォーマンスが向上するため、プログラム内で with ステートメントを使用することは避けてください。

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

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

try{

doSomething()

}キャッチ(例){

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

}


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

試してください{

doSomething()

}キャッチ(例){

handleError(ex); //プロセッサメソッドに委任します

}


最適化されたコードでは、catch 句で実行されるコードは handleError メソッドだけです。この関数は例外オブジェクトをパラメータとして受け取るため、エラーをより柔軟かつ均一に処理できます。実行されるステートメントは 1 つだけであり、ローカル変数にはアクセスされないため、スコープ チェーンの一時的な変更はコードのパフォーマンスに影響しません。

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