ホームページ ウェブフロントエンド jsチュートリアル JavaScript_Basics でのスコープとコンテキストの使用法の概要

JavaScript_Basics でのスコープとコンテキストの使用法の概要

May 16, 2016 pm 05:10 PM
コンテクスト 範囲

JavaScript のスコープとコンテキストは、JavaScript がもたらす柔軟性のおかげで、この言語に固有です。各関数には異なる変数コンテキストとスコープがあります。これらの概念は、JavaScript のいくつかの強力なデザイン パターンの基礎となっています。ただし、これは開発者に大きな混乱をもたらします。以下では、JavaScript におけるコンテキストとスコープの違いと、さまざまなデザイン パターンでそれらがどのように使用されるかを包括的に明らかにします。

コンテキストとスコープ

最初に明確にする必要があるのは、コンテキストとスコープは異なる概念であるということです。長年にわたり、多くの開発者がこれら 2 つの用語を混同し、一方をもう一方と誤って説明していることに気づきました。公平を期すために言うと、これらの用語は非常にわかりにくいものになっています。

各関数呼び出しには、スコープとそれに関連付けられたコンテキストがあります。基本的に、スコープは関数ベースであり、コンテキストはオブジェクトベースです。言い換えれば、スコープは各関数呼び出しでの変数へのアクセスに関連しており、各呼び出しは独立しています。コンテキストは常にキーワード this の値であり、現在の実行可能コードを呼び出すオブジェクトへの参照です。

変数スコープ

変数はローカル スコープまたはグローバル スコープで定義でき、その結果、異なるスコープからランタイム変数にアクセスできます。グローバル変数は関数本体の外側で宣言する必要があり、実行中のプロセス全体に存在し、どのスコープでもアクセスおよび変更できます。ローカル変数は関数本体内でのみ定義され、関数呼び出しごとに異なるスコープを持ちます。このトピックは呼び出し内のみの値の割り当て、評価、操作であり、スコープ外の値にはアクセスできません。

現在、JavaScript はブロックレベルのスコープをサポートしていません。ブロックレベルのスコープとは、if ステートメント、switch ステートメント、loop ステートメントなどのステートメント ブロック内の変数の定義を指します。これは、ステートメントの外部で変数にアクセスできないことを意味します。ブロック。現在、ステートメント ブロック内で定義されている変数は、ステートメント ブロックの外部からアクセスできます。ただし、let キーワードが ES6 仕様に正式に追加されたため、これは間もなく変更されます。ローカル変数をブロックレベルのスコープとして宣言するには、var キーワードの代わりにこれを使用します。

「この」コンテキスト

コンテキストは通常​​、関数の呼び出し方法によって異なります。関数がオブジェクトのメソッドとして呼び出される場合、これはメソッドが呼び出されるオブジェクトに設定されます:

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

var object = {
foo: function(){
alert(this === object); >};

object.foo(); // true


new 演算子を使用してオブジェクトのインスタンスを作成する関数を呼び出す場合にも同じ原則が適用されます。この方法で呼び出すと、this の値は新しく作成されたインスタンスに設定されます:


function foo(){
alert(this);

foo() // ウィンドウ
new foo() // foo


アンバインド関数を呼び出すと、これはデフォルトでグローバル コンテキストまたはウィンドウ オブジェクト (ブラウザー内の場合) に設定されます。ただし、関数が strict モード (「use strict」) で実行される場合、この値はデフォルトで undefine に設定されます。
実行コンテキストとスコープ チェーン

JavaScript はシングルスレッド言語です。つまり、ブラウザーで同時に実行できる処理は 1 つだけです。 JavaScript インタープリターが最初にコードを実行するとき、最初はデフォルトでグローバル コンテキストが使用されます。関数が呼び出されるたびに、新しい実行コンテキストが作成されます。

ここでの「実行コンテキスト」という用語は、上で説明したコンテキストではなくスコープを意味します。これは不適切な命名ですが、この用語は ECMAScript 仕様によって定義されており、それに従う以外に選択肢はありません。

新しい実行コンテキストが作成されるたびに、スコープ チェーンの先頭に追加され、実行スタックまたは呼び出しスタックにもなります。ブラウザは常に、スコープ チェーンの最上位にある現在の実行コンテキストで実行されます。完了すると、それ (現在の実行コンテキスト) はスタックの最上位から削除され、制御は前の実行コンテキストに戻ります。例:



コードをコピー コードは次のとおりです。 function first(){
2番目( );
関数2番目(){
3番目();
関数3番目(){
関数4番目();何か
}
}
}
}
first();


前のコードを実行すると、ネストされた関数が上から下に 4 番目の関数まで実行されます。このとき、スコープ チェーンは上から下に 4 番目、3 番目、2 番目、1 番目、グローバルになります。 4 番目の関数は、グローバル変数と、独自の変数と同様に、1 番目、2 番目、および 3 番目の関数で定義された変数にアクセスできます。 4 番目の関数の実行が完了すると、4 番目のコンテキストがスコープ チェーンの先頭から削除され、実行は 3 番目の関数に戻ります。このプロセスは、すべてのコードの実行が完了するまで継続されます。

異なる実行コンテキスト間の変数名の競合は、ローカルからグローバルまでスコープ チェーンを登ることによって解決されます。これは、同じ名前のローカル変数がスコープ チェーン内でより高い優先順位を持つことを意味します。

簡単に言えば、関数実行コンテキストで変数にアクセスしようとするたびに、検索プロセスは常に独自の変数オブジェクトから開始されます。探している変数が独自の変数オブジェクト内に見つからない場合は、スコープ チェーンの検索を続けます。スコープ チェーンをたどり、各実行コンテキスト変数オブジェクトを調べて、変数名に一致する値を見つけます。

クロージャ

入れ子の関数がその定義 (スコープ) の外でアクセスされ、外側の関数が戻った後に実行できるようにすると、クロージャが形成されます。これ (クロージャ) は、外部関数のローカル変数、引数、関数宣言への (内部関数内での) アクセスを維持します。カプセル化により、実行コンテキストを外部スコープから隠して保護しながら、さらなる操作を実行できるパブリック インターフェイスを公開できます。簡単な例は次のようになります。
コードをコピーします。 コードは次のようになります。

function foo() {
var local = 'プライベート変数';
return function bar(){
return local;
}

var getLocalVariable = foo() ;
getLocalVariable() // プライベート変数

最も一般的なクロージャーの種類の 1 つは、よく知られたモジュール パターンです。これにより、パブリック、プライベート、および特権メンバーを模擬することができます:

コードをコピーします コードは次のとおりです:
var Module = (function(){
var privateProperty = 'foo';

function privateMethod(args){
//何かをする
}

return {

publicProperty: "",

publicMethod: function(args){
//何かをする
},

privilegedMethod: function(args) ){
privateMethod(args);
}
}
})();

モジュールは実際には括弧のペアを追加することでシングルトンに似ています。説明の最後に プロセッサが解釈を終えたらすぐに実行する(関数をすぐに実行する)。クロージャ実行コンテキストで使用できる外部メンバーは、返されたオブジェクトのパブリック メソッドとプロパティ (Module.publicMethod など) だけです。ただし、実行コンテキストは保護され (クロージャ)、変数との対話はパブリック メソッドを通じて行われるため、すべてのプライベート プロパティとメソッドはプログラムのライフ サイクルを通じて存在します。

別のタイプのクロージャは、即時呼び出し関数式 IIFE と呼ばれます。これは、ウィンドウ コンテキストで自己呼び出しされる匿名関数にすぎません。

コードをコピー コードは次のとおりです:
function(window){

var a = 'foo', b = 'bar';

function private(){
// 何かを実行します
}

window.Module = {

public: function(){
// 何かをします
}
}

})(this);グローバル名前空間。この式は非常に便利です。関数本体内で宣言されたすべての変数はローカル変数であり、クロージャを通じてランタイム環境全体にわたって持続します。ソース コードをカプセル化するこの方法は、プログラムとフレームワークの両方で非常に一般的であり、通常は外部と対話するための単一のグローバル インターフェイスを公開します。

Call と apply


は、カスタム コンテキストで関数を実行できるようにするすべての関数に組み込まれている 2 つの単純なメソッドです。 call 関数にはパラメータ リストが必要で、apply 関数ではパラメータを配列として渡すことができます。


コードをコピー
コードは次のとおりです: function user(first, last, age){ // 何かをします
}
user.call(window, 'John', 'Doe' , 30);
user.apply (ウィンドウ, ['ジョン', 'Doe', 30]);
実行結果は同じです。ユーザー関数はウィンドウ コンテキストで呼び出され、同じ 3 つのパラメーターが提供されます。

ECMAScript 5 (ES5) では、コンテキストを制御する Function.prototype.bind メソッドが導入され、この関数 (コンテキスト) は、バインド メソッドの最初のパラメーターに永続的にバインドされます。関数の呼び出し方法。クロージャを通じて関数のコンテキストを修正します。サポートされていないブラウザの場合の解決策は次のとおりです。
コードをコピーします。 コードは次のとおりです。

if(!('bind' in Function.prototype)){
Function.prototype.bind = function(){
var fn = this, context = argument[ 0] 、args = Array.prototype.slice.call(arguments, 1);
return function(){
return fn.apply(context, args);
}


これは、オブジェクト指向やイベント処理などのコンテキスト損失でよく使用されます。これが必要なのは、ノードの addEventListener メソッドがイベント ハンドラーがバインドされているノードとして関数実行のコンテキストを常に維持するためであり、これが重要です。ただし、高度なオブジェクト指向技術を使用し、コールバック関数のコンテキストをメソッドのインスタンスとして維持する必要がある場合は、コンテキストを手動で調整する必要があります。これはバインドによってもたらされる利便性です:


関数MyClass() {
this.element = document.createElement('div');
this.element.addEventListener('click', this.onClick.bind(this), false);
🎜>
MyClass.prototype.onClick = function(e){
// do something
};


バインド関数のソース コードを振り返ると、次の行は、配列のメソッドを呼び出す比較的単純なコードであることに気づくかもしれません:


コードをコピー コードは次のとおりです: Array.prototype.slice.call(arguments, 1);

興味深いことに、ここで、引数オブジェクトは実際には配列ではないことに注意してください。多くの場合、配列のような ) オブジェクトとして説明されます。これは、nodelist (document.getElementsByTagName() メソッド) によって返される結果です。これらには長さ属性が含まれており、値にインデックスを付けることができますが、スライスやプッシュなどのネイティブの配列メソッドをサポートしていないため、配列ではありません。ただし、配列と同様に動作するため、配列メソッドを呼び出してハイジャックすることができます。配列のようなコンテキストで配列メソッドを実行する場合は、上記の例に従ってください。
他のオブジェクト メソッドを呼び出すこの手法は、JavaScript で古典的な継承 (クラス継承) をエミュレートするときにオブジェクト指向にも適用されます:



コードをコピー コードは次のとおりです。 MyClass.prototype.init = function(){
// 「MyClass」のコンテキストでスーパークラスの init メソッドを呼び出します。 " インスタンス
MySuperClass.prototype.init.apply(this, argument);
}


サブクラス (MyClass) のインスタンスでスーパークラス (MySuperClass) のメソッドを呼び出すことによって、この強力なデザインパターンを再現できます。

結論

最新の JavaScript ロールではスコープとコンテキストが重要かつ基本的な役割を果たすため、高度なデザイン パターンを学習し始める前にこれらの概念を理解することが非常に重要です。クロージャ、オブジェクト指向、継承、またはさまざまなネイティブ実装について話す場合でも、コンテキストとスコープが重要な役割を果たします。 JavaScript 言語をマスターし、そのコンポーネントを深く理解することが目標の場合、スコープとコンテキストを出発点にする必要があります。

翻訳者の補足

作者が実装したバインド関数は、bind によって返される関数を呼び出すときにパラメータを渡すことができません。次のコードはこの問題を修正します。 🎜>



コードをコピーします
コードは次のとおりです: if(!('bind' Function.prototype)){ Function.prototype.bind = function(){
var fn = this, context = argument[0], args = Array.prototype.slice.call(arguments, 1);
return function(){
return fn.apply(context, args.concat(arguments));//修正
}
}
}

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

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

C言語でのtypedef構造体の使い方 C言語でのtypedef構造体の使い方 May 09, 2024 am 10:15 AM

typedef struct は、構造体の使用を簡素化するために構造体型のエイリアスを作成するために C 言語で使用されます。構造体の別名を指定することで、新しいデータ型を既存の構造体に別名付けします。利点としては、可読性の向上、コードの再利用、型チェックなどが挙げられます。注: エイリアスを使用する前に構造体を定義する必要があります。エイリアスはプログラム内で一意であり、宣言されているスコープ内でのみ有効である必要があります。

Javaで期待される変数を解決する方法 Javaで期待される変数を解決する方法 May 07, 2024 am 02:48 AM

Java における変数の期待値の例外は、変数の初期化、null 値の使用、およびローカル変数のスコープの認識によって解決できます。

JSのクロージャーの長所と短所 JSのクロージャーの長所と短所 May 10, 2024 am 04:39 AM

JavaScript クロージャーの利点には、変数スコープの維持、モジュール化コードの有効化、遅延実行、およびイベント処理が含まれますが、欠点としては、メモリ リーク、複雑さの増加、パフォーマンスのオーバーヘッド、およびスコープ チェーンの影響が挙げられます。

C++ で include は何を意味しますか C++ で include は何を意味しますか May 09, 2024 am 01:45 AM

C++ の #include プリプロセッサ ディレクティブは、外部ソース ファイルの内容を現在のソース ファイルに挿入し、その内容を現在のソース ファイル内の対応する場所にコピーします。主に、コード内で必要な宣言を含むヘッダー ファイルをインクルードするために使用されます。たとえば、標準入出力関数を組み込むための #include <iostream> などです。

C++ スマート ポインター: ライフサイクルの包括的な分析 C++ スマート ポインター: ライフサイクルの包括的な分析 May 09, 2024 am 11:06 AM

C++ スマート ポインターのライフ サイクル: 作成: スマート ポインターは、メモリが割り当てられるときに作成されます。所有権の譲渡: 移動操作を通じて所有権を譲渡します。リリース: スマート ポインターがスコープ外に出るか、明示的に解放されると、メモリが解放されます。オブジェクトの破壊: ポイントされたオブジェクトが破壊されると、スマート ポインターは無効なポインターになります。

C++ での関数の定義と呼び出しはネストできますか? C++ での関数の定義と呼び出しはネストできますか? May 06, 2024 pm 06:36 PM

できる。 C++ では、ネストされた関数の定義と呼び出しが可能です。外部関数は組み込み関数を定義でき、内部関数はスコープ内で直接呼び出すことができます。ネストされた関数により、カプセル化、再利用性、スコープ制御が強化されます。ただし、内部関数は外部関数のローカル変数に直接アクセスすることはできず、戻り値の型は外部関数の宣言と一致している必要があります。内部関数は自己再帰的ではありません。

Vueのletとvarの違い Vueのletとvarの違い May 08, 2024 pm 04:21 PM

Vue では、let と var の間で変数を宣言するときのスコープに違いがあります。 スコープ: var にはグローバル スコープがあり、let にはブロック レベルのスコープがあります。ブロックレベルのスコープ: var はブロックレベルのスコープを作成しません。let はブロックレベルのスコープを作成します。再宣言: var は同じスコープ内の変数の再宣言を許可しますが、let は許可しません。

js の this が指す状況がいくつかあります。 js の this が指す状況がいくつかあります。 May 06, 2024 pm 02:03 PM

JavaScript では、this のポインティング タイプには、1. グローバル オブジェクト、2. 関数呼び出し、4. イベント ハンドラー、5. アロー関数 (this の外側の継承) が含まれます。さらに、bind()、call()、および apply() メソッドを使用して、これが何を指すかを明示的に設定できます。

See all articles