JavaScriptの"WHERE "と"How "機能に着目して"this "を理解する


この記事では、私はどこでどのように知っているかについて話しますthis 与えられた関数のポイント.基本的に、これは私があなたと共有する方法です.
はい、私はトップでその奇妙な図面を行った😀
第一に、それを理解することが重要ですthis binding 関数が宣言されたときには定義されません.代わりに、関数が呼び出されたときに決定され、また、その関数がどのように呼び出されたかに基づいて決定されます.

ステップ1:どこ


最初に我々がする必要があるのは、関数がどこで我々のプログラムで呼び出されたかを見つけることです.これは、グローバル実行コンテキストまたはローカル実行コンテキストのいずれかから呼び出される可能性がありますし、我々の関数のコールサイトを見つけるための唯一の方法(直接我々のコードで見ている以外)はcall stack . ここでは、スタックを見るためにコンソールで試すことができる非常に簡単な例を示します.
まず、ブラウザのコンソールに次のコードをコピーして貼り付けます.
function baz() {
    bar()
}

function bar() {
    foo()
}

function foo() {
    debugger
}

baz()
次に、devToolで、ソースタブの下に、呼び出しスタックセクションの下に、関数の一覧が表示されます.このように、foo ()を確実に知ることができます.call-site is bar ()およびbar ()call-site と同様である.call-site グローバル実行コンテキストです.
foo         (VM431:10)
bar          (VM431:6)
baz          (VM431:2)
(anonymous) (VM431:13) 

ここで我々の機能を見つける方法を知っていますthis binding どうやって.

ステップ2:どのように


関数が呼び出されると、新しいローカル実行コンテキストが作成されます.ローカル実行コンテキストには、関数に関する情報(呼び出しスタック内の位置、引数の長さ、およびその他のものの間の情報)がありますthis ).
の値this (どのオブジェクトが指しているかは、関数がどのように呼び出されるかに基づいて決定されます.
4つの異なる規則に従って、4つの異なる方法で関数を呼び出すことができます.
  • デフォルト
  • 暗黙の拘束
  • 明示的結合
  • 新しい結合
  • 私はまた、どのようにthis binding は矢印の関数で決定される.

    デフォルト


    var x = 20
    
    function foo() {
      console.log(this.x)
    }
    
    foo.x = 40
    
    foo()  // 20 
    
    
    エーdefault binding のように通常の関数呼び出しを行いますfoo() . インnon-strict モードthis binding グローバルオブジェクトを参照するstrict mode であろうundefined .
    最初の行で変数を宣言する必要がありますx と値を代入します.閉じるこの動画はお気に入りから削除されていますwindow.x = 20 . 長い物語短い、プロパティは、グローバルオブジェクトに作成され、これが理由ですthis.x は20です.
    foo が呼び出されると、このようなものがフードの下で起こります.
    foo.call(window)   // non-strict
    
    foo.call(undefined)   // strict
    
    私たちが後で4つの規則のうちの1つでこの主題を再訪するとしても、私は簡単に説明しますcall() ここで行う方法call() メソッドは、オブジェクトを明示的に設定しますthis にバインドされます.

    暗黙の拘束


    オブジェクトのコンテキストで関数を呼び出すときthis はそのオブジェクトを指します.次のコードを見てみましょう.
    var x = 20 
    
    const myObj = {
      x: 50,
      foo: function() {
         console.log(this.x)
      }
    }
    
    myObj.foo() // 50
    
    での匿名関数宣言をmyObj.foo ( AKメソッドは、オブジェクト内で宣言されているので)myObj . 関数はcallable objects , それらはコピーによって割り当てられる原始的な値とは異なり、参照(すべてのオブジェクトのように)によって割り当てられます.
    私のポイントを説明するには、次のコードを考えます.
    var x = 20 
    
    const myObj = {
      x: 50,
      foo: function() {
         console.log(this.x)
      }
    }
    
    myObj.foo()  // 50
    
    const foo = myObj.foo
    foo()  // 20
    
    
    宣言するときconst foo , 同じ関数への参照を割り当てるmyObj.foo そして、それから、単独の呼び出しをすることによってfoo , デフォルトのバインド規則は適用されず、strict-mode , thisglobal object , この場合、window .
    あなたが見ることができるようにthis 関数が宣言されたときには定義されていませんが、関数が起動され、その関数がどのように呼び出されたかについて最も重要なことになっています.

    明示的結合


    すべての関数は、3つの異なるメソッドにアクセスでき、それらを呼び出して明示的にオブジェクトを設定できますthis にバインドされます.について話していますcall() , apply() and bind() メソッド.
    次のコードを考えます.
    const obj = {
      x: 'Hi there'
    }
    
    function foo(name, age) {
      console.log(
        `${this.x}, my name is ${name}, and I'm ${age} years old`
      )
    }
    
    foo.call(obj, 'Diego', 31)  
    // 'Hi there, my name is Diego, and I'm 31 years old'
    
    foo.apply(obj, ['Diego', 31])  
    // 'Hi there, my name is Diego, and I'm 31 years old'
    
    const bar = foo.bind(obj, 'Diego', 31)
    bar()  // 'Hi there, my name is Diego, and I'm 31 years old'
    
    
    それぞれのスニペットの呼び出し方法について話しましょう.
  • call () :明示的にバインドされるオブジェクトを呼び出して受け取ります(最初のパラメータとして)this . コンマで区切られた関数の引数も受け取ります.
  • apply () : call ()と同じですが、配列内で引数が渡される点が違います.
  • bind () : call ()と似ていますが、関数を直ちに呼び出す代わりに関数を返しますthis 最初の引数として渡されるオブジェクトにバインドします.このスニペットでは、返された関数をconst そして、その下で我々は呼び出しを行います.
  • 新しい結合


    との関数呼び出しnew 最初のキーワードをconstructor call . では、次のコードスニペットを考えてみましょう.
    function foo(name, age) {
       this.name = name
       this.age = age
    }
    
    const bar = new foo('Diego', 31)
    
    console.log(
    `My name is ${bar.name}, and I'm ${bar.age} years old`
    ) 
    
    // My name is Diego, and I'm 31 years old
    
    
    我々がするときconstructor call fooメソッドでは、次のようになります.
  • まず、新しいオブジェクトを作成して返します.好きなものObject.create({}) .
  • this は新たに作成されたオブジェクトを指します.bar .
  • そして、最後に、新しくつくられたオブジェクトは機能のプロトタイプにリンクされます.つまり、bar オブジェクトデリゲート[[Prototype]] / __proto__foo 's prototype オブジェクト.

  • ちょうどリフレッシュとして、すべての機能はprototype オブジェクト.それは1つのプロパティだけです.constructor , これは関数自体へのリファレンスである.
    foo.prototype
    /*
    Output:
    
    { constructor: ƒ foo(name, age), __proto__: Object.prototype }
    */
    
    bar.__proto__    
    
    // or
    
    Object.getPrototypeOf(bar)
    
    /* 
    Output:
    
    { constructor: ƒ foo(name, age), __proto__: Object.prototype }
    */
    
    foo.prototype === bar.__proto__  // true
    foo.prototype === Object.getPrototypeOf(bar) // true
    
    これらは、4つのルールを決定するthis binding 関数です.だから今、我々は我々がどこに知っているために自分自身を尋ねる必要がある質問を知っているthis が指している.

  • この関数はどこで呼び出されましたか?

  • どのように機能が呼び出されましたか?
  • 矢印関数とこれ


    しかし、もう一つ考えることがあります.
    上記の4つの規則とは異なりthis binding 矢印の関数は親のスコープで決定される.つまり、this binding 矢印関数はコンテナ関数と同じです.
    var name = 'Global'
    
    function foo() {
    
      const bar = () => {
          console.log(this.name)
      }
    
      return bar
    }
    
    const obj = {
      name: 'Diego'
    }
    
    const fn = foo()
    fn()  // 'Global'
    
    const fn2 = foo.call(obj)
    fn2()  // 'Diego'
    
    
    foo 関数は呼び出され、this からfoo .
    インconst fn = foo() 以来foo() 呼び出しは通常の/通常の関数呼び出しで、デフォルトのバインド規則が適用されます.この場合FOOのthis へのポイントwindow オブジェクトを返しますstrict mode であろうundefined ).
    でも、const fn2 = foo.call(obj) , 明示的結合ルールは適用されます.なぜなら、fooのthis , これはobj オブジェクト.
    そして、私たちがfn2() (返される矢印関数を呼び出す)4つの規則に従ってデフォルトのバインドであり、それらの規則を無視し、this binding fooの呼び出しの場合、この場合obj .

    最終語


    私が乞食において言ったように、このポストは私がYDKJS本シリーズから特に学んだものを書いていますthis & Object Prototypes ブック・フロムKyle Simpson . 私は完全にシリーズからすべての書籍をお勧めします.