thisは実は簡単です


この文章は主に「あなたの知らないJavaScript上巻」にまとめられています.thisの文章はもうぼろぼろになっていますが、この文章がそれらを助けてくれることを願っています.
前言
thisキーワードはJavaScriptの中で最も複雑なメカニズムの一つであり、特殊なキーワードではなく、すべての関数のスコープに自動的に定義されています.
古い決まりです.直接例を見ます.
function identify(){
    console.log(this.name)
    return this.name.toUpperCase();
}

function speak() {
  var gretting = 'Hello I am '+identify.call(this)
  console.log(gretting);
}

var me = {
    name:'Neal'
}

var you = {
    name:'Nealyang'
}
identify.call(me);
identify.call(you);

speak.call(me);
speak.call(you);
運行結果については、自分で運転して調べてもいいです.もしthisがどのように働いているのかについては、ここでまだ疑問があります.焦らないでください.もちろん今後も深く検討してみます.ここで、thisについての誤解点を紹介します.
thisについての誤解
thisは自分自身に値する.
一般的に新米の人は、thisが関数そのものを指すと考えていますが、なぜ関数に自分自身を引用するのかというと、再帰というケースが存在するからでしょう.しかしここでは、thisは関数そのものを指すのではないと言いたいです.
function foo(num) {
  console.log("foo:"+num);
  this.count++;
}

foo.count = 0;

for(var i = 0;i<10;i++){
    foo(i);
}

console.log(foo.count);
上記のコードを実行すると、foo関数は確かに10回呼び出されましたが、this.com untはfoo.com untに追加されていないようです.つまり、関数のthis.comはfoo.com untではありません.
だから、ここで覚えておきたいのは、関数のthisは関数自体を指すものではないということです.上のコードは以下のように修正されました.
function foo(num) {
  console.log("foo:"+num);
  this.count++;
}

foo.count = 0;

for(var i = 0;i<10;i++){
    foo.call(foo,i);
}

console.log(foo.count);
上記のコードを実行すると、foo関数のcountは確かに10になりました.
thisは彼のスコープに値する.
もう一つのthisに対する誤解はどのように関数の作用域を指すか分かりません.実はある意味では彼は正しいと言えますが、別の意味ではこれは確かに誤解です.
はっきり言って、thisは関数の語法作用領域をいかなる方式で指すことはできません.作用領域はすべての利用可能な識別子を属性の対象としているようです.これは内部からは正しいですが、JavaScriptコードはこの作用領域の「対象」にアクセスできません.これはエンジン内部の実装です.
function foo() {
    var a = 2;
    this.bar();
}

function bar() {
    console.log( this.a );
}

foo(); //undefined
上のコードは間違っています.ここでは議論しないでコードを見るだけです.まず、ビューのthis.bar()はBar関数にアクセスします.確かに彼はできました.たまたまですが.しかし、このコードを書いた開発者ビューは、fooとbarの語法作用領域にthisを用いて橋を建設し、そうであるbarはfoo内部変数作用領域aにアクセスすることができる.もちろん、これは不可能です.this引用を使って語法のスコープの中でものを探すことは不可能です.
何がthisですか
だから、このようなコーディネーターのthisに対する誤解は、いったい何なのでしょうか?メモしてください.thisは作成時にバインドされたものではなく、実行時にバインドされたコンテキストで環境を実行します.thisバインディングと関数の説明は関係なく、むしろ関数が呼び出される方式と関係があります.
関数が呼び出されると、アクティブレコードが作成され、実行環境にもなります.この記録には、関数が何処から呼び出されたか、関数がどのように呼び出されたか、どのようなパラメータが伝達されたかなどの情報が含まれています.この記録の属性の一つは、関数実行中に使用されるthis参照です.
thisはいったいどんな鬼に値するのか、よく分かります.
コールポイント
thisの指向問題を徹底的に理解するためには、何が呼び出されたのか、すなわち関数が呼び出された位置が分かります.コールスタック(現在の実行位置に到達しても、dで呼び出されるすべての方法スタック)は非常に重要であり、関心のあるコールポイントは現在の実行関数の前の呼び出しである.
function baz() {
    //     : `baz`
    //        global scope(     )

    console.log( "baz" );
    bar(); //  `bar`
    //         `baz`

    console.log( "bar" );
    foo(); //  `bar` -> `foo`
    //         `bar`

    console.log( "foo" );
}

baz(); // 
上のコードは、簡単に何がコールスタックとコールポイントですか?比較的簡単なものです.
規則をつけると,決まりがある.
私たちは呼び出し点を検討して、次の四中ルールのどれが適用されるかを判断しなければなりません.4つのルールのそれぞれを独立して解釈してから、複数のルールがコールポイントに適用される場合の優先度を説明します.
標準バインディング
デフォルトのバインディングとは、独立関数の呼び出し形式です.
function foo() {
    console.log( this.a );
}

var a = 2;

foo(); // 2
なぜ2なのかというと、fooを呼び出したときに、JavaScriptはthisにデフォルトのバインディングを施していますので、thisはグローバルオブジェクトを指しています.
ここはデフォルトのバインディングが適用されるとどうやって分かりますか?私たちは呼出点を調べて、foo()がどのように呼び出されたかを確認します.私たちのコードセグメントでは、foo()は、直接的に白白され、無修正の関数によって呼び出されます.他のルールはここに適用されますので、デフォルトのバインディングはここで適用されます.
厳密なモードにおいては、デフォルトのバインディンググローバルオブジェクトは合法的ではなく、thisはundefinedとして設定されていることに留意されたい.しかし、非常に微妙なことは、たとえすべてのthisバインディング規則が呼び出し点に基づいているとしても、fooの内容が厳密なモードでなければ、デフォルトバインディングは合法的である.
バインディングを隠す
呼び出しポイントに環境オブジェクトがあるかどうかは、所有者や容器オブジェクトにもなります.
function foo() {
    console.log( this.a );
}

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

obj.foo(); // 2
fooが説明されて、その属性にobjが付加されると、foo()が最初からobjで宣言されているかどうかに関わらず、後に引用として追加されても、この関数はobjによって「所有」または「包含」されている.
ここで注意したいのは、オブジェクト属性参照チェーンの最後の層だけが呼び出しポイントに影響を及ぼします.
function foo() {
    console.log( this.a );
}

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

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

obj1.obj2.foo(); // 42
バインディングして殺す
thisバインディングの最も頭が痛いところは、彼のバインディングを暗黙的になくしてしまいました.実は呼び出し位置を明確にしました.これも難点ではありません.コードを直接見る
function foo() {
    console.log( this.a );
}

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

var bar = obj.foo; //     !

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

bar(); // "oops, global"
したがって、上記のような呼び出しパターンは、デフォルトのバインディングモードに戻りました.
まだholdに住んでいます.コードを見ます.
function foo() {
    console.log( this.a );
}

function doFoo(fn) {
    // `fn`    `foo`      

    fn(); // 
パラメータ伝達はただ暗黙的な賦値であり、また私たちは関数を伝達しているので、彼は暗黙的な引用賦値です.だから最終結果は前のコードと同じです.
したがって、コールバック関数でthisバインディングを失うことはよくあることですが、私たちのコールバックを受け入れる関数が故意にthisの値を変更する場合もあります.これらの人気のあるイベントはJavaScriptパッケージを扱っています.強制的にあなたの返事をするthisがトリガイベントのDOM要素を指します.
いずれかの予期せぬ方法でthisを変更しても、あなたのコールバック関数参照がどのように実行されるかを本当に制御できないので、あなたは意図的なバインディングをあなたにポイントを制御することができません.私たちはすぐにこの問題を解決する方法を見ます.
上記のように、私達がクリアしなければならないのは参照と呼び出しです.覚えてください.thisを探してください.呼び出しだけを見て、引用に惑わされないでください.
バインディングを明確にする
JavaScriptでは、実行時のthis値を強制的に制定することができます.はい、callとappy、彼らの役割は関数を拡張して生きていく役割です.
function foo() {
    console.log( this.a );
}

var obj = {
    a: 2
};

foo.call( obj ); // 2
上のコードはfooを使って、強制的にfooのthisをobjに指定します.
簡単な元のタイプの値(string,bollan,またはnumberタイプ)をthisとして結びつけると、この元のタイプの値はその対象のタイプ(それぞれnew String(.),new Boolean(.),またはnew Number(.)に包装されます.これは通常「ボックスニング」と呼ばれる.
しかし,個々の依存性が明確に結合されても,以前に述べた問題のためには,良好な解決策,すなわち関数が本来のthisバインディングを失うことができない.
ハードバインド
function foo() {
    console.log( this.a );
}

var obj = {
    a: 2
};

var bar = function() {
    foo.call( obj );
};

bar(); // 2
setTimeout( bar, 100 ); // 2

// `bar` `foo` `this`    `obj`
//          
bar.call( window ); // 2
私たちは関数bar()を作成して、その内部で手動でfoo.cal(obj)を呼び出して、これによって強制的にthisをobjに結びつけてfooを呼び出します.関数barを後にどんなに呼び出しても、Objを手動で使用してfooを呼び出します.このようなバインディングは明確で堅固なので、ハードバインディングと呼ばれています.
newバインディング
これは比較的簡単で、関数の前にnewキーワードを入れて呼び出した場合、つまり構造関数として呼び出されます.その内部には次のようなことができています.
  • 新しいオブジェクトが作成されます.
  • この新たに作成されたオブジェクトは、プロトタイプチェーン
  • に接続されます.
  • この新たに作成されたオブジェクトは、関数として呼び出されるthisバインディング
  • に設定されます.
  • は、関数が他のオブジェクトに戻っていない限り、このnewによって呼び出された関数は、新たに作成されたオブジェクト
  • に自動的に戻ります.
    総括的に一つの問題がある
  • 関数がnewで呼び出されたかどうか、そうであれば、thisバインディングされたのは、新たに作成されたオブジェクト
  • である.
    var bar = new Foo();
  • 関数は、call、appyまたは他の硬度によって呼び出されたかどうか、そうであれば、thisバインディングされたのは指定されたオブジェクト
  • である.
    var bar = foo.call(obj);
  • 関数は、あるコンテキストオブジェクトで呼び出されたかどうか、もしそうなら、thisバインディングされたのは、そのコンテキストオブジェクト
  • である.
    var bar = obj.foo();
  • が全部でない場合は、デフォルトのバインディングを使用して、厳密なモードではundefinedにバインドされています.ここでは方法の中の厳しい声明です.そうでないと、グローバルオブジェクト
  • にバインドされます.
    var bar = foo();
    バインディング例外
    第一の場合はnullとundefinedをcall、apply、bindなどの関数に伝達します.このときthisが採用するバインディング規則はデフォルトバインディングです.
    第二の場合、ここで例を挙げると、面接でよく出る例です.
    function foo() {
      console.log(this.a);
    }
    var a = 2;
    var o = {
        a:3,
        foo:foo
    }
    var p = {a:4};
    (p.foo = o.foo)();
    上記のように、fooはデフォルトのバインディングを採用しています.ここでは、p.foo=o.fooの戻り値は目標関数の参照です.最後の文はfooです.
    s 6の矢印関数
    s 6の矢印関数は簡単で、矢印関数はFnctionキーで定義されていないので、矢印関数はthisのこの4つのルールを適用しないで、外層関数または大域作用領域に基づいてthisを決定します.
    function foo() {
      //     arrow function
        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を自動的に取得します.
    経典の面接試験問題を一つください.
  • 第1題
  • var a=10;
    var foo={
      a:20,
      bar:function(){
          var a=30;
          console.log(this)
          return this.a;
        }
    };
    foo.bar()
    (foo.bar)()
    (foo.bar=foo.bar)()
    (foo.bar,foo.bar)()
  • 第二題
  • function t(){
    this.x=2;
    }
    t();
    console.log(window.x);
  • 第三問
  • var obj = {
    x: 1,
    y: 2,
    t: function() {
    console.log(this.x)
    }
    }
    obj.t();
    
    var dog={x:11};
    dog.t=obj.t;
    dog.t();
    
    
    show=function(){
    console.log('show'+this.x);
    
    }
    
    dog.t=show;
    dog.t();
  • 第四題
  • name = 'this is window';
    var obj1 = {
    name: 'php',
    t: function() {
    console.log(this.name)
    }
    };
    var dog1 = {
    name: 'huzi'
    };
    
    obj1.t();
    
    dog1.t = obj1.t;
    
    var tmp = dog1.t;
    tmp(); //this    window
    
    (dog1.t = obj1.t)();
    dog1.t.call(obj1);
  • 第5題
  • var number=2;
    var obj={
    number:4,
    /*      */
    fn1:(function(){
    var number;
    this.number*=2;//4
    
    number=number*2;//NaN
    number=3;
    return function(){
    var num=this.number;
    this.number*=2;//6
    console.log(num);
    number*=3;//9
    alert(number);
    }
    })(),
    
    db2:function(){
    this.number*=2;
    }
    }
    
    var fn1=obj.fn1;
    
    alert(number);
    
    fn1();
    
    obj.fn1();
    
    alert(window.number);
    
    alert(obj.number);
    交流
    スキャンコードは私の個人のWeChat公式アカウントに注目して、もっと多いオリジナルの文章を共有します.交流学習をクリックして、WeChat、qq群を追加してください.一緒に勉強して、一緒に進歩します.上のテーマを共有しましょう.
    兄弟の参加を歓迎します.
    Node.js技術交流群:209530601
    React技術スタック:398240621
    先端技術雑談:604953717(新規作成)