JavaScriptスコープについて知りたいことすべて(ただし、尋ねるのが怖かった)

JSにはスコープに関連するいくつかの概念がありますが、初心者の開発者(および経験豊富な開発者でも)には必ずしも明確ではありません。 この記事は、スコープ、クロージャ、「this」、ネームスペース、関数のスコープ、グローバル変数、レキシカルスコープ、プライベートおよびパブリックエリアなどの言葉を聞いて、JSスコープの深intoに飛び込もうとする人々に捧げられています。資料を読んで、次の質問に答えることができます。



-スコープとは何ですか?

-グローバル/ローカルOSとは何ですか?

-名前空間とは何ですか?OBとはどう違いますか?

-このキーワードの意味と、OBとの関係は?

-機能的および語彙的OBとは何ですか?

-閉鎖とは何ですか?

-このすべてをどのように理解して作成できますか?



スコープとは何ですか?





JSでは、スコープはコード内の現在のコンテキストです。 OBはローカルまたはグローバルに定義できます。 防弾コードを書く鍵は、OBを理解することです。 変数と関数が利用できる場所、コード内のコンテキストを変更し、より高速でよりサポートされたコード(デバッグが高速)を記述する方法を理解しましょう。 OMに対処するのは簡単です。AかBのどちらのOMに今入っているのかという質問を自問します。



グローバル/ローカルOBとは何ですか?





1行のコードを記述することなく、すでにグローバルOBにいます。 変数をすぐに定義する場合、変数はグローバルOBにあります。



//   var name = 'Todd';
      
      







グローバルOBはあなたの親友であり、最悪の悪夢です。 異なるエージェントと連携することを学ぶと、名前の共通部分が表示されない限り、グローバルエージェントの問題は発生しません。 多くの場合、「グローバルOB-これは悪い」と聞くことができますが、その理由の説明はほとんど得られません。 GOV-悪くはありません。異なるOBから利用できるモジュールとAPIを作成するときに使用する必要があります。利益を得るために慎重に使用する必要があります。



私たちはすべてjQueryを使用しました。 書いたらすぐに



 jQuery('.myClass');
      
      







グローバルOBでjQueryにアクセスし、このアクセスを名前空間と呼ぶことができます。 用語「名前空間」は、用語OBの代わりに使用される場合がありますが、通常はレベル自体のOBを示します。 この場合、jQueryはグローバルOBにあり、名前空間です。 jQuery名前空間は、jQueryライブラリのPIとして機能するグローバルOBで定義されますが、そのすべてのコンテンツはこのPIから継承されます。



ローカルOBとは何ですか?





ローカルOMは、グローバルの後に定義されたOMと呼ばれます。 通常、1つのGOVがあり、定義された各関数にはローカルOBが含まれます。 別の関数内で定義された各関数には、外部関数のOBに関連付けられた独自のローカルOBがあります。



関数を定義して内部で変数を設定すると、それらはローカルOBに属します。 例:



 //  A:  var myFunction = function () { //  B:  };
      
      







BWTのすべての変数はGOWに表示されません。 外部から直接アクセスすることはできません。 例:



 var myFunction = function () { var name = 'Todd'; console.log(name); // Todd }; // ReferenceError: name is not defined console.log(name);
      
      







変数「name」はローカルOBを参照します。外部からは見えないため、定義されていません。



機能的OB。





すべてのローカルOBは、機能OBでのみ作成され、forループやwhileループ、またはifやswitchなどのディレクティブによっては作成されません。 新しい機能は新しいスコープです。 例:



 //  A var myFunction = function () { //  B var myOtherFunction = function () { //  C }; };
      
      







そのため、新しいOBとローカル変数、関数、オブジェクトを簡単に作成できます。



レキシカルOB





1つの関数が別の関数の内部で定義されている場合、内部は外部OMにアクセスできます。 これは、「字句OB」または「クロージャ」または「静的OB」と呼ばれます。



 var myFunction = function () { var name = 'Todd'; var myOtherFunction = function () { console.log('My name is ' + name); }; console.log(name); myOtherFunction(); //   }; // : // `Todd` // `My name is Todd`
      
      







レキシカルOBを使用するのは非常に簡単です。親のOBで定義されているものはすべて、子のOBで使用できます。 例:



 var name = 'Todd'; var scope1 = function () { // name   var scope2 = function () { // name   var scope3 = function () { // name   ! }; }; };
      
      







逆方向では、これは機能しません。



 // name = undefined var scope1 = function () { // name = undefined var scope2 = function () { // name = undefined var scope3 = function () { var name = 'Todd'; //   }; }; };
      
      







「名前」へのリンクはいつでも返すことができますが、変数自体は返せません。



OBシーケンス





OBのシーケンスは、選択した機能のOBを決定します。 定義された各関数には独自のOMがあり、別の内部で定義された各関数には独自のOMがあり、外部OMに接続されています-これはシーケンスまたはチェーンです。 コード内の位置は、OBを定義します。 変数の値を決定すると、JSは探している関数、オブジェクト、または変数が見つかるまで、最も深い埋め込みOBから外側に向かっていきます。



短絡





彼らは語彙OBと密接な同盟関係にあります。 良い使用例は、関数参照を返すことです。 内部で定義されたものにアクセスできるようにするさまざまなリンクを引き出すことができます。



 var sayHello = function (name) { var text = 'Hello, ' + name; return function () { console.log(text); }; };
      
      







テキストを表示するには、sayHello関数を呼び出すだけでは不十分です。



 sayHello('Todd'); // 
      
      







関数は関数を返すので、最初に関数を割り当ててから呼び出す必要があります。



 var helloTodd = sayHello('Todd'); helloTodd(); //     'Hello, Todd'
      
      







もちろん、直接クロージャを引き起こすことができます:



 sayHello('Bob')(); //    
      
      







AngularJSは、$ compileメソッドで同様の呼び出しを使用します。この場合、現在のOBへのリンクを渡す必要があります。



 $compile(template)(scope);
      
      







簡単に言うと、コードは次のようになっていると推測できます。



 var $compile = function (template) { //   //    scope return function (scope) { //      `template`   `scope` }; };
      
      







関数は、クロージャーになるために何も返す必要はありません。 現在のOBの外部から変数にアクセスすると、クロージャーが作成されます。



OVと「これ」





各OBは、関数の呼び出し方法に応じて、「this」変数に値を割り当てます。 私たちはすべてthisキーワードを使用しましたが、それがどのように機能し、呼び出しの違いが何であるかを全員が理解しているわけではありません。 デフォルトでは、最も外側のOBのオブジェクトである現在のウィンドウを参照します。 さまざまな呼び出しがこの値をどのように変更するかの例:



 var myFunction = function () { console.log(this); // this = , [ Window] }; myFunction(); var myObject = {}; myObject.myMethod = function () { console.log(this); // this =   { myObject } }; var nav = document.querySelector('.nav'); // <nav class="nav"> var toggleNav = function () { console.log(this); // this =  <nav> }; nav.addEventListener('click', toggleNav, false);
      
      







この値には問題もあります。 次の例では、同じ関数内で値とOBを変更できます。

 var nav = document.querySelector('.nav'); // <nav class="nav"> var toggleNav = function () { console.log(this); // <nav> element setTimeout(function () { console.log(this); // [ Window] }, 1000); }; nav.addEventListener('click', toggleNav, false);
      
      







ここでは、イベントハンドラから呼び出されない新しいOBを作成しました。つまり、ウィンドウオブジェクトを参照します。 たとえば、混乱しないように、この値を別の変数に保存できます。



 var nav = document.querySelector('.nav'); // <nav class="nav"> var toggleNav = function () { var that = this; console.log(that); //  <nav> setTimeout(function () { console.log(that); //  <nav> }, 1000); }; nav.addEventListener('click', toggleNav, false);
      
      







.call()、. apply()、および.bind()を使用してOBを変更します





必要に応じてOSを変更する必要がある場合があります。

例では:



 var links = document.querySelectorAll('nav li'); for (var i = 0; i < links.length; i++) { console.log(this); // [ Window] }
      
      







この値は反復要素には適用されず、何も呼び出したり、OBを変更したりしません。 OBを変更する方法を見てみましょう(より正確には、関数呼び出しのコンテキストを変更します)。



.call()および.apply()





.call()および.apply()メソッドにより、OBを関数に渡すことができます。



 var links = document.querySelectorAll('nav li'); for (var i = 0; i < links.length; i++) { (function () { console.log(this); }).call(links[i]); }
      
      







その結果、反復された要素の値がこれに渡されます。 .callメソッド(scope、arg1、arg2、arg3)はコンマで区切られた引数のリストを受け取り、.apply(scope、[arg1、arg2])メソッドは引数の配列を受け取ります。



.call()または.apply()メソッドは関数を呼び出すことを覚えておくことが重要です。



 myFunction(); //  myFunction
      
      







let .call()関数を呼び出してパラメーターを渡します。



 myFunction.call(scope);
      
      







.bind()





.bind()は関数を呼び出しませんが、呼び出す前に変数の値を単にバインドします。 ご存じのとおり、関数参照にパラメーターを渡すことはできません。



 //  nav.addEventListener('click', toggleNav, false); //      nav.addEventListener('click', toggleNav(arg1, arg2), false);
      
      







これは、新しいネストされた関数を作成することで修正できます。



 nav.addEventListener('click', function () { toggleNav(arg1, arg2); }, false);
      
      







しかし、ここでもOMに変更があり、追加の関数が作成され、パフォーマンスに悪影響を及ぼします。 したがって、.bind()を使用します。その結果、引数を渡して関数が呼び出されないようにすることができます。



 nav.addEventListener('click', toggleNav.bind(scope, arg1, arg2), false);
      
      





プライベートおよびパブリックエージェント





JavaScriptでは、他の多くの言語とは異なり、パブリックおよびプライベートOSの概念はありませんが、クロージャーを使用してそれらをエミュレートできます。 プライベートOBを作成するには、他の関数で関数をラップします。



 (function () { //    })();
      
      







機能を追加します。



 (function () { var myFunction = function () { //  ,   }; })();
      
      







ただし、この関数を直接呼び出すことはできません。



 (function () { var myFunction = function () { //  ,   }; })(); myFunction(); // Uncaught ReferenceError: myFunction is not defined
      
      







ここにプライベートOBがあります。 パブリックOVが必要な場合は、次のトリックを使用します。 このモジュールに関連するすべてを含むモジュール名前空間を作成します。



 //   var Module = (function () { return { myMethod: function () { console.log('myMethod has been called.'); } }; })(); //    Module.myMethod();
      
      







returnディレクティブは、グローバルOBで公開されているメソッドを返します。 さらに、これらは目的のネームスペースに関連しています。 モジュールには、必要な数のメソッドを含めることができます。



 //   var Module = (function () { return { myMethod: function () { }, someOtherMethod: function () { } }; })(); //    Module.myMethod(); Module.someOtherMethod();
      
      







グローバルOMのすべてのメソッドをダンプして汚染する必要はありません。 これは、関数を返さずにプライベートOBを編成する方法です。



 var Module = (function () { var privateMethod = function () { }; return { publicMethod: function () { } }; })();
      
      







publicMethodを呼び出すことはできますが、privateMethodを実行することはできません-プライベートOBを参照します。 これらの関数には、addClass、removeClass、Ajax / XHR呼び出し、配列、オブジェクトなど、好きなものを何でも入れることができます。



興味深いひねりは、1つのOB内ですべての関数が他のOBにアクセスできるため、パブリックメソッドからグローバルOBで使用できないプライベートメソッドを呼び出すことができることです。



 var Module = (function () { var privateMethod = function () { }; return { publicMethod: function () { //     `privateMethod`: // privateMethod(); } }; })();
      
      







これにより、コードの対話性とセキュリティが向上します。 安全のために、すべての関数をグローバルOBにダンプする価値はないため、呼び出す必要のない関数が誤って呼び出されることはありません。



プライベートメソッドとパブリックメソッドを使用してオブジェクトを返す例:



 var Module = (function () { var myModule = {}; var privateMethod = function () { }; myModule.publicMethod = function () { }; myModule.anotherPublicMethod = function () { }; return myModule; // returns the Object with public methods })(); //  Module.publicMethod();
      
      







パブリックメソッドとパブリックメソッドを視覚的に区別するために、プライベートメソッドの名前に下線を付けると便利です。



 var Module = (function () { var _privateMethod = function () { }; var publicMethod = function () { }; })();
      
      







リスト内のメソッドを返し、関数へのリンクを返すことも便利です。



 var Module = (function () { var _privateMethod = function () { }; var publicMethod = function () { }; return { publicMethod: publicMethod, anotherPublicMethod: anotherPublicMethod } })();
      
      






All Articles