私の知らないjavaScript--私を困らせたthisは問題を指すのが意外にもこんなに簡単です.


私はjavaScriptに接触したばかりの時、thisについては、一度タッチするのが怖いという問題でしたが、実はthisは簡単でよく分かります.一言で言えば、thisの値は関数実行時に確認されます.
一、バインディング規則
1、普通の関数(window)—デフォルトのバインディング
function fn1() {
    console.log(this.a)
}
var a = 1;
fn1()   // 1
    fn 1が起動されると()の時、this.aは大域変数aに解析されました.ここのfn 1()です.は、任意の修飾なしの関数参照を直接使用して呼び出されるので、デフォルトのバインディングのみを使用して、thisはグローバルオブジェクトに向けられます.    しかし、標準バインディングは非厳密モードでのみグローバルオブジェクトにバインディングされます.厳密モードでは、fn 1()の呼び出し位置に関係なく
"use strict";
function fn1() {
    console.log(this.a)
}
var a = 1;
fn1(); // Uncaught TypeError: Cannot read property 'a' of undefined
2、オブジェクト方法として呼び出される(オブジェクト自体に戻す)—陰的バインディング
const name = '  ';
function sayHi() {
    console.log(this.name);
}
const obj = {
    name: '  ',
    sayHi
}
obj.sayHi();    //   
    まず、sayHi()は、直接objにおいて定義されても先に定義されても参照属性に追加されても、この関数は厳密にはobjオブジェクトには含まれません.sayHi()呼び出された時、その着地点は確かにobjオブジェクトを指します.関数がコンテキストオブジェクトを参照すると、このコンテキストオブジェクトに関数呼び出し中のthisをバインドします.sayHi()を呼び出すとthisがobjに結合されるので、this.nameはobj.nameと同じです.
const name = '  ';
function sayHi() {
    console.log(this.name);
}
const obj = {
    name: '  ',
    sayHi
}
const objOut = {
    name: '  ',
    obj
}
objOut.obj.sayHi(); //   
オブジェクト属性参照チェーンの最後の層だけが呼び出し位置に影響を及ぼします.以下は最も一般的なthisバインディング問題です.つまり、バインディングされた関数がバインディングされているオブジェクトをなくします.つまり、デフォルトバインディングが適用されます.したがって、thisをグローバルオブジェクトまたはundefinedに結びつけるのは、厳密なモードかどうかによって異なります.
const name = '  ';
function sayHi() {
    console.log(this.name)
}
const obj = {
    name: '  ',
    sayHi
}
let sayHello = obj.sayHi;
sayHello(); //   
sayHelloはobj.sayHiの一つの引用であるが、実際にはsayHi関数自体を参照しているので、Bar()は実際には何も修飾されていない関数として呼び出されているので、デフォルトのバインディングを適用したものであり、より一般的であり、より意外な場合は着信コール関数で発生する:
const name = '  ';
function sayHi() {
    console.log(this.name)
}
function anotherSayHi(fn) {
    fn();
}
const obj = {
    name: '  ',
    sayHi
}
let sayHello = obj.sayHi;
anotherSayHi(obj.sayHi) //   
パラメータ伝達は実は一種の隠蔽的な割り当てであり、前の例と同じです.コールバック関数が失われたthisバインディングは非常に一般的であり、コールバック関数の関数はthisを変更することがあります.
3、callを使って、appy、bind(何が入ってきましたか?)ーバインディングを表示します.
const a = 1;
function fn2() {
    console.log(this.a)
}
const obj = {
    a: 2
}
fn2.call(obj) // 2
fn 2 call(…)により、fn 2を呼び出したときに、Objに強制的にそのthisを結びつけることができます.ハードバインディング
const a = 1;
function fn2() {
    console.log(this.a);
}
const obj = {
    a: 2
}
let fn3 = function() {
    fn2.call(obj);
}
fn3(); // 2
fn3.call(window) // 2
fn 3=>を作成しました.fn 2 calを内部で手動で呼び出します.したがって、fn 2のthisをobj=>に強制的に結びつけた後、fn 3=>を呼び出します.これは常に手動でobj上でfoo=>という明示的な強制バインディングです.つまり、ハードバインディングといいます.
4、class方法で呼び出し(現在のインスタンス自体)—newバインディング
newを使用して関数を呼び出したり、構造関数の呼び出しが発生した場合、自動的に次の操作が実行されます.新しいオブジェクトを作成します.この新しいオブジェクトは「プロトタイプ」接続=>この新しいオブジェクトが関数で呼び出されたthis=>関数が他のオブジェクトに戻っていない場合、new式の関数呼び出しが自動的にこの新しいオブジェクトに戻ります.
const name = '  ';
class People {
    constructor(name) {
        this.name = name
        this.age = 20
    }
    sayHi() {
        console.log(this.name)
    }
}
const lisi = new People('  ')
lisi.sayHi() //   
二、優先度
上記の例を通して、thisバインディングのルールが分かりました.必要なのは関数の呼び出し位置を見つけて、どのルールを適用するかを判断することです.しかし、ある呼び出し位置に複数のルールが適用されます.どうすればいいですか?彼らの優先度を知る必要があります.暗黙的なバインディングと明示的なバインディングのどちらが優先度が高いですか?
const a = 1;
function fn1() {
    console.log(this.a);
}
const obj1 = {
    a: 2,
    fn1
};
const obj2 = {
    a: 3,
    fn1
};
obj1.fn1(); // 2
obj2.fn1(); // 3

obj1.fn1.call(obj2); // 3
obj2.fn1.call(obj1); // 2
バインディングの優先度が高いと表示されます.隠しバインディングとnewバインディングのどちらが優先度が高いですか?
function foo(something) {
    this.a = something;
}
const obj1 = {
    foo
};
const obj2 = {};
obj1.foo(2);
console.log(obj1.a); // 2
obj1.foo.call(obj2, 3)
console.log(obj2.a); // 3

const bar = new obj1.foo(4);
console.log(obj1.a) // 2
console.log(bar.a) // 4
newバインディングの優先度が高いことが分かります.newとcall/applyは一緒に使えないので、new foo.cal(obj 1)で直接テストすることができません.しかし,ハードバインディングを用いてその2人の優先度を試験することができた.
function foo(something) {
    this.a = something;
}
const obj1 = {};
const bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2
const baz = new bar(3);
console.log(obj1.a); // 2
console.log(baz.a); // 3
barはObj 1にハードバインドされ、newはハードバインディング(Obj 1への)呼び出しbar(…)の中のthisを修正しました.newバインディングを使用して、bazという新しいオブジェクトが得られました.そして、baz.aの値は3です.優先度で上記のどのルールを適用すべきかを判断できます.
  • newバインディング
  • call,appy(バインディング表示)
  • 暗黙的バインディング
  • デフォルトバインディング
  • 三、特殊なES 6矢印関数
    矢印関数は、4つの標準的なバインディング規則を使用するのではなく、現在の語法のスコープに基づいてthisを決定し、矢印関数は外層関数で呼び出されたthisバインディングを継承します.
    const zhangsan = {
        name: '  ',
        sayHi() {
            // this      
            console.log(this);
        },
        wait() {
            // this      
            console.log(this);
            setTimeout(function () {
                // this === window
                // setTimeout           ,   zhangsan.sayHi()  
                console.log(this);
            });
        },
        waitAgain() {
            // this      
            console.log(this);
            setTimeout(() => {
                // this      
                //     this        this
                console.log(this);
            });
        }
    };
    zhangsan.sayHi();
    zhangsan.wait();
    zhangsan.waitAgain();