JavaScript Thisに対する理解を深める

5323 ワード

私のブログへようこそ.「JavaScript Thisに対する理解を深める」
JavaScriptに関するthisの話はもうたくさん見たと思います.注文した以上、続けて見てもいいです.thisに対する理解を深めてくれますか?
最近『You Dotent Know JS』という本を読んでいますが、JSの長年の先端を使ってこの本を読んでも、多くの収穫があると思います.thisについての説明は、私の理解を深めてくれました.知識点を整理し、自分の理解を加えて、自分の言語で説明します.読者にとっては中古の知識です.この本はオープンソースです.本書のGithubプロジェクトの住所に行って、片手の知識を学ぶことができます.
まず皆がよく知っている言葉があります.私はもう一度強調します.「thisは関数が呼び出された時に発生したバインディングです.何を指すかは関数がどこで呼び出されるかによって決まります.」
この文はとても重要で、これはthisの原理を理解する基礎です.thisを説明する前に、作用域に関する概念を理解しておく必要があります.
「語法作用域」と「動態作用域」
通常、作用域は全部で二つの主要な作業モデルがあります.
  • 語法の作用領域
  • ダイナミックスコープ
  • 語法のスコープはほとんどのプログラミング言語で採用されているモードですが、ダイナミックスコープではまだいくつかのプログラミング言語が使用されています.例えば、Bashスクリプトです.JavaScriptは採用された語法の作用域であり、つまりプログラミング段階では、役割領域が明確になってきています.
    次のコードを考える:
    function foo(){
      console.log(a);   //    2
    }
    
    function bar(){
      let a = 3;
      foo();
    }
    
    let a = 2;
    
    bar()
    
    JavaScriptが使用するのは語法作用領域であるため、自然thisが宣言した段階では、変数foo()の作用領域が確定されている.
    JavaScriptが採用されているダイナミックレンジである場合、aにおいて印刷されたものはfoo()である.
    function foo(){
      console.log(a);   //    3 (   2)
    }
    
    function bar(){
      let a = 3;
      foo();
    }
    
    let a = 2;
    
    bar()
    
    JavaScriptの3は、動作時に呼び出された場所で動的に結合された動的スコープと似ている.
    thisの4つの結合規則
    JavaScriptでは、thisの指向に影響を与えるバインディングルールは4つあります.
  • デフォルトバインディング
  • 暗黙的バインディング
  • 明示バインディング
  • newバインディング
  • 標準バインディング
    これは最も直接的な方法です.つまり、修繕符をつけないで直接関数を呼び出すことです.
    function foo() {
      console.log(this.a)   //    a
    }
    
    var a = 2;  //            
    
    foo();
    
    thisを使用して宣言された変数varは、グローバルオブジェクトに紐付けられ、ブラウザであればaオブジェクトである.windowが呼び出したとき、デフォルトのバインディングを参照して、foo()はグローバルオブジェクトを指す.
    陰式バインディング
    この場合は呼び出し位置に「コンテキストオブジェクト」がある場合に発生します.
    function foo() {
      console.log(this.a);
    }
    
    let obj1 = {
      a: 1,
      foo,
    };
    
    let obj2 = {
      a: 2,
      foo,
    }
    
    obj1.foo();   //    1
    obj2.foo();   //    2
    
    関数が起動されると、コンテキストオブジェクトがある場合、thisは、コンテキストオブジェクトにバインドされる.上記のコードのように、thisが呼び出されたとき、obj1.foo()thisにバインドされ、obj1が呼び出されたとき、obj2.foo()thisにバインドされている.
    明示的なバインディング
    このように、obj2の3つの方法Function.prototypecall()apply()を使用している.これらの3つの関数は、関数のbind()が指定されたオブジェクトに向けられていてもよく、違いは、thisおよびcall()が即座に関数を実行し、許容されるパラメータの形式が異なることである.
  • apply()
  • call(this, arg1, arg2, ...)
  • apply(this, [arg1, arg2, ...])は、新しい包装関数を作成し、すぐに実行するのではなく、リターンする.
  • bind()
  • bind(this, arg1, arg2, ...)は、パラメータの形式を受信し、関数ネスティング関数を容易にすると、apply()変数を次の階層関数に伝達する.
    次のコードを考える:
    function foo() {
      console.log(this.a);  //    1
      bar.apply({a: 2}, arguments);
    }
    
    function bar(b) {
      console.log(this.a + b);  //    5
    }
    
    var a = 1;
    foo(3);
    
    上記のコードでは、arguments内部のfoo()は、デフォルトのバインディング規則に従い、グローバル変数にバインディングされている.thisは、呼び出し時にbar()関数を呼び出し、apply()を新しいオブジェクトthisに結び付け、そのまま受信した{a: 2}の関数です.
    newバインディング
    最後の一つは、foo()オペレータを使用してnewのバインディングを生成することである.thisオペレータのnewに対する影響を理解するには、まずthisの原理を理解する必要がある.JavaScriptでは、newオペレータは、他の対象言語とは違って、アナログされたメカニズムである.JavaScriptでは、すべての関数をnewで呼び出すことができます.このとき、この関数は一般的に「構造関数」と呼ばれます.実際にはいわゆる「構造関数」は存在しません.より正確な理解は、関数に対する「構造呼び出し」です.newを使用して関数を呼び出すと、自動的に以下の動作が実行されます.
  • 新しいオブジェクトを作成します.
  • この新しいオブジェクトは[Prottype]接続されます.
  • この新しいオブジェクトは、関数呼び出しのthisに結合されます.
  • 関数が他のオブジェクトに戻っていない場合、new式の関数呼び出しは自動的にこの新しいオブジェクトに戻ります.
  • したがって、newが関数であれば、このようになります.
    function New(Constructor, ...args){
        let obj = {};   //        
        Object.setPrototypeOf(obj, Constructor.prototype);  //            
        return Constructor.apply(obj, args) || obj;   //     ,   this       
    }
    
    function Foo(a){
        this.a = a;
    }
    
    New(Foo, 1);  // Foo { a: 1 }
    
    したがって、newを使用して関数を呼び出すと、新しいオブジェクトを構築し、関数呼び出しのnewに結合します.
    優先度
    一つの位置にthisを変更するルールが複数発生した場合、優先度はどうなりますか?
    いくつかのコードを見ます
    //      >     
    function foo() {
        console.log(this.a);
    }
    
    let obj1 = {
        a: 2,
        foo,
    }
    
    obj1.foo();     //    2
    obj1.foo.call({a: 1});      //    1
    
    これは「明示的バインディング」の優先度が「暗黙的バインディング」より大きいことを示しています.
    // new    >     
    function foo(a) {
        this.a = a;
    }
    
    let obj1 = {};
    
    let bar = foo.bind(obj1);
    bar(2);
    console.log(obj1); //    {a:2}
    
    let obj2 = new bar(3);
    console.log(obj1); //    {a:2}
    console.log(obj2); //    foo { a: 3 }
    
    これは「newバインディング」の優先度が「明示的バインディング」よりも大きく、「デフォルトバインディング」の優先度が最も低いことを示している.優先順位は以下の通りです.
    「newバインディング」>「明示バインディング」>「陰式バインディング」>「標準バインディング」
    だから、thisは一体何ですか?thisは、作成時にバインディングされたものではなく、実行時にバインディングされたものである.そのコンテキストは、関数呼び出し時の様々な条件に依存します.thisのバインディングと関数宣言の位置は関係なく、関数の呼び出しに依存します.関数が呼び出されると、「実行コンテキスト」が作成されます.このコンテキストは、関数がどこで呼び出されるか(コールスタック)、関数の呼び出し方法、着信するパラメータなどの情報を含みます.thisは、この記録の属性の一つであり、関数実行中に使用される.
    参照
    You Dott Know JS-this&Object Prottotypes