JSの中のThisはもう聞かれないでください.面接でよくある問題分析です.


GitHubアドレス:https://github.com/SimonZhang...
thisの指し示す問題はフロントエンドの皆が頭が痛い問題であるべきです.私も同じです.最近は「JavaScript」や「JavaScript言語の精粋とプログラミング実践」などの書籍を読んでいます.だからこの文章を書いて、私の心得を共有してください.
陰式バインディング
thisに関しては、一般的に、誰がメソッドを呼び出したのか、この方法のthisは誰を指していますか?
function foo(){
    console.log(this.a)
}

var a = 3;

var obj = {
    a: 2,
    foo: foo
};

obj.foo(); //   2,   obj   foo,  foo this   obj, obj.a = 2
複数の呼び出しがある場合、 、例えば:
function foo() {
    console.log( this.a )
}

var obj2 = { 
    a: 42,
    foo: foo
}

var obj1 = {
    a: 2,
    obj2: obj2
}

obj1.obj2.foo(); // 42
陰でなくします
最も一般的なthisバインディング問題は、 の関数によってバインディングされたオブジェクトが失われることであり、すなわち彼はデフォルトのバインディングに応じて、thisをグローバルオブジェクトまたはundefinedに結びつけることができます.厳格なモードかどうかによって異なります.
function foo() {
    console.log( this.a )
}

var obj1 = {
    a: 2,
    foo: foo
}

var bar = obj1.foo; //     !

var a = "oops, global"; // a        

bar(); // "oops, global"
bar obj.foo , , foo , bar() , より微妙であり、より一般的であり、予想外の状況が で発生した.
function foo() {
    console.log( this.a )
}

function doFoo( fn ){
    // fn        foo
    fn(); // 
、したがって、私たちは関数に入っても暗黙的に割り当てられますので、結果は前の例と同様に、関数が自分の宣言に入ってきた関数(setTimeoutなど)ではなく言語内の関数に入ってくると、 になります.
明示的なバインディング
簡単に言うと、コール、appy、bind、newバインディングなどのように、thisを指定します.
ハードバインディング
function foo( something ) {
    console.log( this.a, something)
    return this.a + something
}

var obj = {
    a: 2
}

var bar = function() {
    return foo.apply( obj, arguments)
}

var b = bar(3); // 2 3
console.log(b); // 5
ここで簡単に説明します.bar関数において、fooはapply関数を使用してobjをバインドしています.つまり、fooのthisはobjを指します.同時に、argments(着信パラメータの数を制限しない)を使って、パラメータとしてfoo関数に入ってきます.ですから、bar(3)を実行する際に、まずObj.a、つまり2と3を出力し、fooは両者の加算値を返しますので、bの値は5です.
同様に、本例はbindを使用してもよい.
function foo( something ) {
    console.log( this.a, something)
    return this.a + something
}

var obj = {
    a: 2
}

var bar = foo.bind(obj)

var b = bar(3); // 2 3
console.log(b); // 5
newバインディング
従来のクラス指向言語では、new初期化類を使用するとクラス中の構造関数が呼び出されますが、JSではnewの仕組みは実際とクラスや言語とは全く違っています.newを使用して関数を呼び出すか、またはコンストラクタコールが発生した場合、自動的に次のような動作が実行されます.
  • 新規オブジェクト
  • を作成します.
  • この新しいオブジェクトが実行されます.
  • この新しいオブジェクトは、関数呼び出しのthis
  • に結び付けられます.
  • 関数が他のオブジェクトに返されない場合、new式の関数は自動的にこの新しいオブジェクトに戻ります.
    function foo(a){
        this.a = a
    }
    
    var bar = new foo(2);
    console.log(bar.a); // 2
    newを使ってfoo(…)を呼び出すと、新しいオブジェクトを作成してfoo(…)呼び出しのthisに結合します.newは関数呼び出し時のthisバインディング挙動に影響を与える最後の方法であり,newバインディングと呼ぶ.
    thisの優先度
    疑いなく、デフォルトのバインディングの優先度は4つの規則の中で最も低いので、それを考慮しなくてもいいです.
    暗黙的なバインディングと明示的なバインディングのどちらが優先度が高いですか?テストしてみます.
    function foo(a){
        console.log(this.a)
    }
    
    var obj1 = {
        a: 2,
        foo: foo
    }
    
    var obj2 = {
        a: 3,
        foo: foo
    }
    
    obj1.foo(); // 2
    obj2.foo(); // 3
    
    obj1.foo.call(obj2); // 3
    obj2.foo.call(obj1); // 2
    は、優先度がより高い、すなわち、明示的なバインディングが存在するかどうかを判断する前に考慮すべきであることが分かる.
    次に、new の優先度の高い人と低い人を明確にします.
    function foo(a){
        this.a = something
    }
    
    var obj1 = {
        foo: foo
    }
    
    var obj2 = {}
    
    obj1.foo(2); 
    console.log(obj1.a); // 2
    
    obj1.foo.call(obj2,3);
    console.log(obj2.a); // 3
    
    var bar = new obj1.foo(4)
    console.log(obj1.a); // 2
    console.log(bar.a); // 4
    new より優先度が高いと見られます.しかし、new のどちらが優先度が高いですか?
    function foo(something){
        this.a = something
    }
    
    var obj1 = {}
    
    var bar = foo.bind(obj1);
    bar(2);
    console.log(obj1.a); // 2
    
    var baz = new bar(3);
    console.log(obj1.a); // 2
    console.log(baz.a); // 3
    new の中のthisを修正しているので、new の優先度は より高いことが分かる.
    newでハードバインディング関数を使用する目的は、関数のいくつかのパラメータを事前に設定することであり、このようにnewを用いて初期化すると残りのパラメータのみが導入される.ビnd(…)の機能の一つは、最初のパラメータ(最初のパラメータをバインディング用のthis)以外のパラメータを下の層の関数に伝えることができることである(この技術は「部分アプリケーション」と呼ばれ、「 」の一種である).例えば:
    function foo(p1,p2){
        this.val = p1 + p2;
    }
    
    //      null                 this   
    //     new this    
    var bar = foo.bind(null,'p1');
    
    var baz = new bar('p2');
    
    baz.val; // p1p2
    }
    コリック化:直感的に、コリック化は「一定のパラメータを固定すれば、残りのパラメータを受け入れる関数が得られる」と主張しています.したがって、2つの変数がある関数yxに対して、y=2が固定されていると、変数がある関数2 xが得られます.
    矢印関数でのThisの適用
    矢印関数は、thisの4つの標準規則を使用せずに、外層(関数または大域)作用領域に基づいてthisを決定します.
    矢印関数の語法のスコープを見てみましょう.
    function foo() {
        //         
        return (a) => {
            // this   foo()
            console.log(this.a)
        };
    }
    
    var obj1 = {
        a: 2
    };
    
    var obj2 = {
        a: 3
    };
    
    var bar = foo.call(obj1);
    bar.call(obj2); // 2,   3!
    foo()内部で作成された矢印関数は、呼び出し時のfoo()のthisをキャプチャします.foo()のthisはobj 1に結合されているので、bar(矢印関数参照)のthisはobj 1にも結合されています.矢印関数のバインディングは変更されません.(newもだめ!)
    締め括りをつける
    実行中の関数のthisバインディングを判断するには、この関数の直接呼び出し位置を見つける必要があります.見つかったら、次の4つの規則を順次適用して、thisのバインディングオブジェクトを判断します.
  • はnewで呼び出されますか?新規作成したオブジェクトに結合します.
  • はcallまたはappy(またはbind)によって呼び出されますか?指定されたオブジェクトにバインドされます.
  • はコンテキストオブジェクトによって呼び出されますか?そのコンテキストオブジェクトに結合します.
  • デフォルト:厳密なモードでundefinedにバインドされ、そうでないとグローバルオブジェクトにバインドされます.