this全面解析(二)

7287 ワード

前のセクションでは、thisの2つのバインド方法について詳しく説明しました. です.このセクションでは、thisの他の2つのバインド方法 new について説明します.では、私たちが解決しなければならない問題はもちろん前節で述べたように、thisが失われています.

明示的なバインド


暗黙的なバインドでは、オブジェクトの内部に関数を指す属性を含め、この属性を介して関数を間接的に参照して、thisをこのオブジェクトに間接的にバインドする必要があります.では、各オブジェクトに関数参照を含めず、各オブジェクトに関数を強制的に呼び出したい場合は、どうすればいいのでしょうか.この場合、call( this, ...)apply( this, ...)の方法が登場する必要があります.この2つの方法の最初のパラメータはthisに用意されています.違いは、他のパラメータの形式で、彼らの他のパラメータの比較は以下の通りです.
call(  this, "  1","  2","  3","  4");
apply(  this, ["  1","  2","  3","  4"]);

applyの他のパラメータは配列シーケンスとして存在し、実行時に単一のパラメータに解析して呼び出した関数に順次渡されますが、これは何の役に立ちますか?配列を追加します.
var arr = [1,2,3,4,5,6];

今、その中の最大値を見つけます.もちろん、ここにはいろいろな方法があります.ここでapplyについて述べた以上、apply法でこの問題を解決します.グループの中で一番大きいのを見つけたいなら、Mathを使う簡単な方法があります.max(...).しかし、この方法では、配列の最大値を見つけることはできません.つまり、
Math.max(1,2,3,4,5); //        5
Math.max([1,2,3,4,5]); //      ,            

私たちのやり方は、
Math.max.apply(null, [1,2,3,4,5]); //         5

pushなど、他にもいろいろな用途がありますが、ちょっと問題が外れているようです!!!しかし、私が言いたいのはcall()とapply()の2つの方法で、thisを指定したオブジェクトに明示的にバインドすることができます!
    function foo(){
        console.log(this.a);
    }
    var obj = {
        a: 2
    }
    foo.call(obj);//2

しかし、明示的なバインドでは、thisがバインドを失った問題は解決できません.
ハードバインド
明示的にバインドされた変種はこの問題を解決することができる.
    function foo(){
        console.log(this.a);
    }
    var obj = {
            a: 2
         }
    var bar = function(){
        foo.call(obj);
    }
    bar();// 2
    setTimeout(bar, 100); // 2
    bar.call(window); // 2

それがどのように動作しているかを見てみましょう.私たちは関数bar()を作成し、彼の内部でfooを手動で呼び出しました.call(obj).したがって,fooのthisをobjに強制的にバインドし,その後どのように関数barを呼び出すかにかかわらず,obj上でfooを手動で呼び出す.このような形式は と呼ばれています.ハードバインドの一般的な適用シーンは、パラメータを受信して値を返すラップ関数を作成することです.
    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

もう1つの一般的な方法は、繰り返し使用できる補助関数を作成することです.
    function foo(something){
        console,log(this.a, something);
        return this.a + something;
    }
    //       
    function bind(fn, obj){
        return function(){
            return fn.apply(obj, arguments);
        }
    }
    var obj = {
        a:2
    }
    
    var bar = bind(foo, obj);

    var b = bar(); //2, 3
    console.log(b); //5

ハードバインドは非常に一般的なモードであるため、ES 5は内蔵の方法Functionを提供する.prototype.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

bind(...)指定したパラメータをthisのコンテキストに設定し、元の関数を呼び出すハードコーディングされた心関数が返されます.

newバインド


第4のルールは、最後のルールでもあります.彼を説明する前に、javascriptの関数とオブジェクトに関する非常に一般的な誤解を明らかにします.従来のクラス向け言語では、「コンストラクション関数」はクラス内のいくつかの特殊な方法であり、newを使用してクラスを初期化すると、クラス内のコンストラクション関数が呼び出されます.通常の形式は次のとおりです.
someThinges = new MyClass(...)

JAvascriptにもnewオペレータがありますが、javascriptのnewオペレータのメカニズムはクラス向けの言語とは全く違います.まず、JavaScrritのコンストラクション関数を再定義します.Javascriptでは、コンストラクション関数はnewオペレータを使用するときに呼び出される関数にすぎません.クラスに属したり、クラスをインスタンス化したりしません.実際には、newオペレータによって呼び出される一般的な関数にすぎない特殊な関数タイプとは言えません.例えば、Number()をコンストラクション関数として考えるときの挙動、ES 5.1では、次のように記述されています.
Number    
 Number new        ,        :          。

したがって、組み込みオブジェクト関数を含むすべての関数をnewで呼び出すことができます.この関数はコンストラクション関数呼び出しと呼ばれます.これは非常に細かい違いがあります.実際には位置する「コンストラクション関数」は存在せず、関数に対する「コンストラクション呼び出し」だけです.newを使用して関数を呼び出すと、次の操作が自動的に実行されます.
  • 新しいオブジェクトを作成
  • この新しいオブジェクトは[[prototype]]接続が実行されます(後で詳しく説明します)
  • この新しいオブジェクトは、関数呼び出しのthis
  • にバインドされます.
  • 関数が他のオブジェクトを返さない場合、new式の関数は自動的にこのオブジェクトを返します.
  •     function foo(a){
            this.a = a
        }
    
        var bar = new foo(2);
        console.log(bar) // foo {a: 2}
        console.log(bar.a); //2

    newを使用してfooを呼び出す(...)を選択すると、新しいオブジェクトを構築しfoo(...)にバインドします.呼び出し中のthisにあります.newは、関数呼び出し時のthisバインド動作に影響を与える最後の方法です.私たちはnewバインドと呼ばれています.

    矢印関数


    私たちが前に紹介した4つのルールには、正常に存在するすべての関数が含まれています.しかし、ES 6では、これらのルールを使用できない特殊な関数タイプが紹介されています.矢印関数矢印関数はfunctionキーワードで定義されるのではなく、"=>"で定義されます.矢印関数は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にバインドされ、矢印関数のバインドは変更できません.矢印関数は、イベントプロセッサやタイマなどのコールバック関数で最も一般的に使用されます.
        function foo(){
            setTimeot(()=>{
                //   this       foo(),      foo()    obj1 ,     this      obj1 
                console.log(this.a)
            },100)
        }
        var obj1 = {
                a: 2
            }
        foo.call(obj1); //2
    

    矢印関数はbind(.)のように使用できます.同様に、関数のthisが指定されたオブジェクトにバインドされていることを確認します.また、従来のthisメカニズムの代わりに、より一般的な文法の役割ドメインを使用することが重要です.実際,ES 6の前に矢印関数とほぼ同じパターンを用いた.
        function foo(){
            console.log(this); //Object {a: 2}
            
            var self = this; //       this
            
            setTimeout(function(){
            console.log(this); // Window {external: Object, chrome: Object, document: document, obj1: Object, obj2: Object…}
           
           console.log(self.a);
            }, 100);
        }
        var obj1 = {
                    a: 2
                }
        foo.call(obj1); //2

    私はそれぞれこのコードの中でfoo()の内部とsetTimeout()の内部に2行のコードconsole.log(this)を加え、foo()関数を呼び出してそれをobj1にバインドした場合(すなわち実行foo.call(obj1))、foo()内のthisはObject {a: 2}であり、説明foo()関数の中のthisはすでにobj1にバインドされ、setTimeout()内の結果はWindow...であり、前節の「this全面解析(一)」の内容を見るとよく理解できるはずです.setTimeout()メソッドでは、関数パラメータは暗黙的な付与に相当し、呼び出し方式は自然に setTimeout()メソッドでは関数のthisがwindowを指すからです.期待される結果を得るために、foo()のthisを保存し(すなわち、var self = this)、次いで、setTimeout()メソッドの関数でself変数を文法的役割ドメインの参照する.読者は自分でテストすることができますが、そうしないと結果は何になりますか(undifinedですか?自分で検証してみましょう!)よし!うっかりくどくど言ってしまった.self=thisと矢印関数はbind()に取って代わることができるように見えるが,本質的にはthisメカニズムに取って代わると考えられる.

    小結


    実行中の関数のthisバインドを判断するには、この関数の直接呼び出し位置を見つける必要があります.見つかったら、次の4つのルールを順番に適用してthisのバインドオブジェクトを判断できます.
  • newによって呼び出されますか?新しく作成したオブジェクトにバインド
  • call()またはapply()によって呼び出されますか?指定したオブジェクトにバインド
  • コンテキストオブジェクトによって呼び出されますか?そのコンテキストオブジェクトにバインド
  • デフォルト:厳格モードundifined、非厳格バインドグローバルオブジェクト
  • ES 6の矢印関数は、4つの標準的なバインドルールを使用するのではなく、文法の役割ドメインに基づいてthisを決定します.具体的には、矢印関数は、ES 6以前のコードのself=thisメカニズムと同様に、外層関数が呼び出したthisバインド(thisが何にバインドされているかにかかわらず)を継承します.