JavaScript`魔法のキーワード`this

5947 ワード

前言thisはJavaScriptコードの中でほとんど随所に見られます.同時に、JavaScriptの中の比較的複雑なメカニズムの一つです.フロントエンドの開発者ははっきりした認識がない場合、thisは完全に魔法であり、つかみにくいです.
グローバルコードのthis
ここではすべてが簡単です.グローバルコードでは、thisは常にグローバルオブジェクト自体を指します.
//            
this.a = 1;
console.log(a); // 1

//         
//              
b = 2;
console.log(this.b); // 2

//         
//              
c = 3;
console.log(this.c); //3
関数コードのthis
1誤解
関数コードでthisを使うのは面白いです.thisを関数自体に向けて容易に理解します.実はそうではないです.次のコードを見てみましょう.
function foo(num) {
    console.log("foo: " + num);

    //   foo      
    this.count++;
}

foo.count = 0;

for (var i = 1; i < 3; i++) {
    foo(i);
}
// foo: 1
// foo: 2

// foo       ?
console.log(foo.count); // 0 -- WTF?

console.log文は確かに2回出力されたが、foo.countは依然として0であり、字面の意味からthisが間違っていることを理解するのは明らかである.foo.count = 0を実行すると、確かに関数オブジェクトfooに属性countが追加されます.しかし、関数内部のthis.countthisは、その関数オブジェクトを指すものではない.では、thisはいったい誰を指していますか?
2 thisとは何ですか?
thisは、動作中にバインディングされており、作成時(定義時)にバインディングされていません.このバインディング規則は、関数呼び出し時の様々な条件に依存します.
2.1標準バインディング
まず、最も一般的な関数コールタイプを紹介します.独立関数コール、すなわち直接 ( )の呼び出し方法です.この規則は他の規則を適用できない場合のデフォルトと見なすことができます.
function foo() {
    console.log(this.a);
}

var a = 2;

foo(); // 2
グローバルスコープ内で宣言された変数var a = 2(グローバルオブジェクトの属性でもある)が、関数foo()の呼び出し時に印刷されたことに留意すべきである.これにより、デフォルトのバインディングのthisはグローバルオブジェクトを指すことがわかった.
PS:厳密なモードであれば、デフォルトのバインディングは、グローバルオブジェクトではなくundefinedとなります.
2.2暗黙的バインディング
関数がオブジェクトとして使用される方法(実はすべての属性)である場合は、暗黙的なバインディング規則になります.
function foo() {
    console.log(this.a);
}

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

obj.foo(); // 2
ここでの関数fooは、オブジェクトobjの属性に参照されたものであり、次いで . ()で呼び出される.このとき、this.aobj.aの値を得ることができ、thisobjを指していることが分かります.(どのオブジェクトによって呼び出したらどのオブジェクトを指すのか)もう一つの暗黙的な損失に注意しています.
function foo() { 
    console.log( this.a );
}

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

var bar = obj.foo; //     !

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

bar(); // "oops, global"
barobj.fooの1つの参照であるが、実際には、2つはfoo関数自体を参照するだけで、bar()はデフォルトのバインディングの呼び出しだけである!暗黙的な損失は通常、コールバック関数で見られます.以下に述べた表示バインディングで解決できます.
2.3明示的なバインディング
関数がcallおよびapply法によって呼び出されるとき、私たちはディスプレイバインディングと呼ぶ.
function foo() {
    console.log(this.a);
}

var obj = {
    a: 2
};

foo.call(obj); // 2
foo.apply(obj); // 2
foo.call(obj)foo.apply(obj)を通して、foothisを指しました.objのバインディング角度から言えば、thiscallは同じです.その違いは他のパラメータに反映されています.
さらに、ES 5には、関数を実行しないように結合関数のapplyだけを指すbind方法が提供されている.
function foo(something) { 
    console.log(this.a, something);
}

var obj = { 
    a: 2
};

var bar = foo.bind(obj);

bar(3); // 2 3 
thisは単にfoo.bind(obj)を結びつけた関数を返しただけで、thisを呼び出した時にはすでにデフォルトのバインディングではなく、この方法を利用して上記の暗黙的な損失問題を解決することができます.
//        
    function foo() { 
        console.log(this.a);
    }

    var obj = { 
        a: 'obj',
        foo: foo
    };
    var a = 'global';

    setTimeout(obj.foo, 100); // "global"

//  bind      
    function foo() { 
        console.log(this.a);
    }

    var obj = { 
        a: 'obj',
        foo: foo
    };
    var a = 'global';

    setTimeout(obj.foo.bind(obj), 100); // "obj"
私たちは、元のコードのbar(3)をコールパラメータとしてobj.fooに伝えたが、setTimeoutの指向がなくなったのはなぜですか?thisは呼び出しの方式によって指し示しが確定されると言っていますが、ここでは関数参照だけを呼び出していません.内部呼び出しの時はデフォルトのバインディング規則が適用されているかもしれません.その後、thisはグローバルオブジェクトを指しています.thisの疑似コードは以下の通りです.
function setTimeout (fn, delay) {
    //   delay  
    fn(); //       ,             
}
setTimeoutをバインディングして返した関数をコールバックパラメータとして使用して、bindが指向を失う問題を解決しました.
2.4 newバインディング
これは最後のバインディング規則で、構造関数を呼び出しにthisを使用すると、newバインディング規則です.
function foo(a) { 
    this.a = a;
} 

var bar = new foo(2);

console.log( bar.a ); // 2
newを使用して関数を呼び出すと、以下のような動作が行われる.
  • 新しいオブジェクトを作成(構成)します.
  • このオブジェクトに対してプロトタイプリンクnewを行う(これは重要ではない)
  • この新しいオブジェクトは、関数呼び出しのbar.__proto__ = foo.prototype、つまりthisthis.a = aに結び付けられ、新しいオブジェクト
  • を指す.
  • 関数が他のオブジェクトに戻っていない場合、この新しいオブジェクト
  • に戻ります.
    全体としてthisを使用してnewを呼び出すと、関数のfoo()は、関数が作成した新しいオブジェクトを指しています.
    2.5優先度
    関数呼び出し中のthisによって結合された4つのルールが分かりました.あなたが必要なのは、関数呼び出しの位置を見つけて、どのルールが適用されているかを判断することです.thisの方向が分かります.しかし、いくつかのルールを適用したら、最終的にどのルールが勝者となりますか?自分で試してみてもいいです.ここでは多く説明しないで、直接に答えを出してください.
    newバインディング>明示的バインディング>陰的バインディング>標準バインディング
    2.6 ES 6の矢印関数
    ES 6における新しい姿勢としては、矢印関数のthisが上記規則以外に凌駕され、外層作用領域によってthisが決定される.
    function foo() {
        setTimeout(() => {
            //    this       foo()
            console.log(this.a);
        }, 100);
    }
    
    var obj = {
        a: 2
    };
    
    foo.call(obj); // 2
    
    明示的なバインディングthisによって内部矢印関数のfooも引き継がれた.実はこれはシンタックス飴にすぎません.プライベート変数を使ってthisを保存して、コードを変換しました.
    function foo() {
        var self = this; //             foo `this`
        setTimeout(function() {
            console.log(self.a);
        }, 100);
    }
    
    var obj = {
        a: 2
    };
    
    foo.call(obj); // 2
    
    でも、書き方ではかっこいいです.ははは!
    おわりに
    全体として、呼び出し位置が見つけられたら、その規則を解析すれば、正確にthisを見つけることができる.初めてブログを書きます.ちょっと乱れているかもしれません.レンガを持って死んでください.
    知識は「あなたの知らないJavaScript」から吸収します.