範囲
範囲は、変数と関数の関数の範囲です。 JavaScriptの関数で宣言されたすべての変数は、関数本文で常に表示されます。 JavaScriptにはグローバルなスコープとローカルスコープがありますが、ブロックレベルのスコープはありません。ローカル変数の優先順位は、グローバル変数の優先度よりも高くなっています。いくつかの例を通して、JavaScriptの範囲の「暗黙のルール」を理解できます(これらは、フロントエンドのインタビューでよく尋ねられる質問でもあります)。
1。変数宣言事前に
例1:
var scope = "global"; function scopetest(){console.log(scope); var scope = "local"} scopetest(); //未定義ここでの出力は未定義であり、エラーはありません。これは、上記の関数の宣言が関数本体に常に表示されるためです。上記の関数は次のと同等です。
var scope = "global"; function scopetest(){var scope; console.log(scope); scope = "local"} scopetest(); //地元VARが忘れられている場合、変数はグローバル変数として宣言されることに注意してください。
2.ブロックレベルのスコープなし
一般的に使用する他の言語とは異なり、JavaScriptにはブロックレベルの範囲はありません。
function scopetest(){var scope = {}; if(scope instanceof object){var j = 1; for(var i = 0; i <10; i ++){//console.log(i); } console.log(i); //出力10} console.log(j); //出力1}JavaScriptでは、変数の関数の範囲は関数レベルです。つまり、関数のすべての変数が関数全体で定義されています。
var scope = "hello"; function scopetest(){console.log(scope); //①var scope = "no"; console.log(scope); //②}①の値出力は実際には定義されていませんでしたが、これはクレイジーです。グローバル変数の値を定義しました。この場所はこんにちはではありませんか?実際、上記のコードは以下と同等です。
var scope = "hello"; function scopetest(){var scope; console.log(scope); // scope = "no"; console.log(scope); //②}宣言の早期変数とグローバル変数は、ローカル変数よりも優先度が低くなります。これら2つのルールによると、出力が未定義の理由を理解することは難しくありません。
スコープチェーン
JavaScriptでは、各関数には独自の実行コンテキストがあります。この環境でコードが実行されると、可変オブジェクトのスコープチェーンが作成されます。スコープチェーンはオブジェクトリストまたはオブジェクトチェーンであり、可変オブジェクトへの整然とアクセスを保証します。
スコープチェーンのフロントエンドは、現在のコード実行環境の変数オブジェクトであり、多くの場合「アクティブオブジェクト」と呼ばれます。変数の検索は、最初のチェーンのオブジェクトから始まります。オブジェクトに変数属性が含まれている場合、検索は停止します。そうでない場合、グローバルオブジェクトが見つかるまで、検索は優れたスコープチェーンを検索し続けます。
スコープチェーンの検索ステップバイステップも、プログラムのパフォーマンスに影響します。変数のスコープチェーンが長いほど、パフォーマンスへの影響が大きくなります。これは、グローバル変数の使用を避けようとする主な理由でもあります。
閉鎖
基本概念
スコープは、閉鎖を理解するための前提条件です。閉鎖は、現在のスコープ内の外部スコープ内の変数にアクセスする機能を指します。
function createclosure(){var name = "jack"; return {setStr:function(){name = "rose"; }、getStr:function(){return name + ":hello"; }}} var builder = new createclosure(); builder.setstr(); console.log(builder.getStr()); //ローズ:こんにちは上記の例では、関数の2つのクロージャーを返します。どちらも外部スコープへの参照を維持するため、外部関数の変数は、呼び出された場所で常にアクセスできます。関数内で定義された関数は、外部関数のアクティブオブジェクトを独自のスコープチェーンに追加します。したがって、上記の例では、内部関数は内部関数を介して外部関数のプロパティにアクセスできます。これは、JavaScriptがプライベート変数をシミュレートする方法でもあります。
注:閉鎖には関数の追加のスコープがあるため(内部匿名関数は外部関数のスコープを運ぶ)、閉鎖は他の機能よりも多くのメモリ空間を占める可能性があり、過剰な使用はメモリ使用量の増加につながる可能性があります。
閉鎖の変数
閉鎖を使用する場合、スコープチェーンメカニズムの影響により、閉鎖は内部関数の最後の値のみを取得できます。これの1つの副作用は、内部関数がループにある場合、変数の値が常に最後の値であることです。
//このインスタンスは合理的ではなく、特定の遅延要因があります。これは主に、閉鎖ループ関数の問題を説明するためです。 }
上記のプログラムは、予想どおり1〜5の数字を入力しませんが、5回すべて5回出力します。別の例を見てみましょう。
function createclosure(){var result = []; for(var i = 0; i <5; i ++){result [i] = function(){return i; }} return result;}CreateClosure()[0]()は5を返し、CreateClosure()[4]()を返す値はまだ5です。上記の2つの例から、ループを使用して内部関数を使用するときに閉鎖が存在する問題を見ることができます。外部関数が戻ると、この時点でのIの値は5であるため、各内部関数Iの値も5です。
では、この問題を解決する方法は?匿名のラッパー(匿名の自己実行関数式)を通じて、予想される結果の返還を強制することができます。
function timemanage(){for(var i = 0; i <5; i ++){(function(num){settimeout(function(){console.log(num);}、1000);})(i); }}または、閉鎖匿名関数の匿名関数の割り当てを返します。
function timemanage(){for(var i = 0; i <10; i ++){(e){return function(){console.log(e);})(i)、1000)}} // timemanager();出力1,2,3,4,5機能CREATECLOSURE(){var result = []; for(var i = 0; i <5; i ++){result [i] = function(num){return function(){console.log(num); } }(私); } return result;} // createclosure()[1]()出力1; createclosure()[2]()出力2原則として、匿名のラッパーであろうとネストされた匿名関数であろうと、関数は値によって渡されるため、変数の値は実際のパラメーターnumにコピーされ、匿名関数が匿名関数内に作成され、各関数にはnumのコピーがあります。
これは閉鎖です
少し不注意が問題を引き起こす可能性があるため、これを閉鎖で使用する場合は特に注意してください。通常、このオブジェクトは実行時に関数にバインドされていることを理解しています。グローバル関数では、このオブジェクトはウィンドウオブジェクトです。関数がオブジェクト内のメソッドと呼ばれる場合、これはこのオブジェクトに等しくなります(TODOはこれについてソートプロセスを行います)。匿名関数の範囲はグローバルであるため、この閉鎖は通常、グローバルオブジェクトウィンドウを指します。
var scope = "global"; var object = {scope: "local"、getscope:function(){return function(){return this.scope; }}}object.getScope()()を呼び出し、予想されるローカルの代わりにグローバル値を返します。閉鎖内の内部匿名関数は外部関数の範囲を運ぶので、外部関数のこれを取得してみませんか?各関数が呼び出されると、これと引数が自動的に作成されます。内部匿名関数を検索するとき、アクティブオブジェクトで必要な変数を検索します。したがって、外部関数の検索を停止すると、外部関数の変数に直接アクセスすることはできません。要するに、関数が閉鎖のオブジェクトの方法として呼ばれる場合、メソッド内の匿名関数でこれがグローバル変数を指していることに特に注意することが重要です。
幸いなことに、この問題を非常に簡単に解決できます。これを外部関数の範囲に保存するだけで、閉鎖によってアクセスできる変数に保存できます。
var scope = "global"; var object = {scope: "local"、getscope:function(){var that = this; return function(){return that.scope; }}} object.getScope()()()()はローカル値を返します。メモリとパフォーマンス
閉鎖には、関数ランタイムコンテキストと同じスコープチェーン参照が含まれているため、特定のマイナス効果があります。アクティブオブジェクトと関数のランタイムコンテキストが破壊されると、アクティブオブジェクトへの参照がまだあるため、アクティブオブジェクトを破壊することはできません。
function bindevent(){var target = document.getElementById( "Elem"); Target.OnClick = function(){console.log(target.name); }}上記の例では、匿名関数は外部オブジェクトターゲットへの参照を生成します。匿名関数が存在する限り、参照は消えず、外部関数のターゲットオブジェクトは破壊されず、円形の参照が作成されます。解決策は、ターゲットのコピーを作成し、オブジェクトを手動でリセットすることにより、外部変数への円形の参照を減らすことです。
function bindevent(){var target = document.getElementById( "Elem"); var name = target.name; Target.onclick = function(){console.log(name); }ターゲット= null; }閉鎖に外部変数へのアクセスがある場合、識別子の検索パスが間違いなく追加され、特定の状況では、パフォーマンスの損失も引き起こします。前述のことを説明しました。外部変数をローカル変数に保存して、スコープチェーンの検索長を短縮するようにしてください。
概要:閉鎖はJavaScriptに固有のものではありませんが、JavaScriptに独自のユニークな症状があります。閉鎖を使用して、JavaScriptのいくつかのプライベート変数を定義し、ブロックレベルのスコープを模倣することさえできます。ただし、閉鎖の使用中に、不必要な問題を回避するために既存の問題を理解する必要もあります。