JavaScript(10):this/call/appy/bind

17032 ワード

記事の目次
  • 一、this
  • .何がthis
  • ですか?
  • .thisは何を表していますか?
  • .thisを結合する方法
  • .thisの指差
  • .変更指示
  • 、Function.prototype.bind()
  • 、call/appy
  • 1.定義
  • .文法
  • .異同
  • 一、this
    1.何がthisですか
    thisキーワードは大部分の言語において重要な存在であり、JSにおいてはもちろん例外ではなく、その表現の意味が豊富で多様であり、複雑であり、thisを深く理解することはJSを学習し、対象に向かってプログラミングすることにとって非常に重要な一環である.
    2.thisは何を表していますか
    thisは関数(方法)が実行するコンテキスト環境(文脈、似たような文章を理解したいなら、文章の文脈を理解してこそ、様々な関係を明確に知ることができる)を表します.
    しかし、JavaScriptの中でthisは固定的ではなく、実行環境の変化によって変化します.
    1.方法では、thisはこの方法が属するオブジェクトを表します.
    2.単独で使用する場合、thisはグローバルオブジェクトを表します.
    3.関数では、thisはグローバルオブジェクトを表します.
    4.関数では、厳密なモードでは、thisは未定義です.
    5.イベントにおいて、thisはイベントを受信する要素を表します.
    6.類似のcall()とappy()の方法は、thisを任意のオブジェクトに引用することができる.
    3.thisを結合する方法
    thisのダイナミックな切り替えは、JavaScriptのために巨大な柔軟性を作り出しましたが、プログラミングが難しくなり、曖昧になりました.時々、思いがけないことが起こらないように、thisを固定する必要があります.JavaScriptは、call、appy、bindの3つの方法を提供して、thisの指向を切り替え/固定します.
    4.thisの指向
    1.一般的な関数方法では、thisを使ってグローバルオブジェクトを指します.
    function test(){
        this.x = 1;  //  this  window
        console.log(this.x);
      }
      test(); // 1
    
    JSでは、関数のthisは、関数が呼び出されたときに決定されます.現在動作している環境を指します.
    2.対象方法として呼び出し、thisは上位の対象を指す.
    var x =3;
    function test(){
      alert(this.x);
    }
    var o = {
      x:1,
      m:test 
    };
    o.m(); // 1
    
    関数をオブジェクトとする方法の場合、メソッドのthisはオブジェクトを指します.
    3.構造関数として呼び出し、thisはnewからのオブジェクトを指します.
    function test(){
        console.log(this);
      }
      var o = new test();
           test();
    //    o         
    
    newキーワードの役割は、ある関数を呼び出し、その中の戻り値を得ることです.ただし、呼び出しプロセスはちょっと特殊です.上記のコードの例では.test関数がnewキーワードで呼び出された場合、内部は以下の手順を順次実行します.
    (1)空のオブジェクトを作成します.
    (2)この空のオブジェクトのプロトタイプを、このコンストラクタのプロトタイプに指します.
    (3)関数内部のthisに空のオブジェクトの値を割り当てます.
    (4)関数体コードを実行し、このオブジェクトにキーペアをバインドします.
    (5)thisを返し、newキーワードとしてoop関数の戻り値を呼び出します.
    だからコンストラクタの中のthisは相変わらず構造関数がnewキーワードに呼び出された時にその方向を確定しています.
    4.矢印関数のthis矢印関数はES 6の新しい特性であり、最も重要な特徴は、そのコンテキストのthisを自分のthisとして捉えることです.あるいは、矢印関数自体はthisではなく、外部環境のthisをそのまま使用しています.つまり、矢印関数の内部は外部のthisと一致しています.
    this.a=20
    var test={
        a:40,
        init:()=>{
            console.log(this.a)
            function go(){
                this.a=60
                console.log(this.a)
            }
            go.prototype.a=50
            return go
        }   
    }
    
    var p=test.init()
    p()
    new (test.init())()
    //   20 60 60 60
    
    5.指向を変える
    thisのダイナミックな切り替えは、JavaScriptのために巨大な柔軟性を作り出しましたが、プログラミングが難しくなり、曖昧になりました.時々、思いがけないことが起こらないように、thisを固定する必要があります.JavaScriptは、call、appy、bindの3つの方法を提供して、thisの指向を切り替え/固定します.
    bindメソッドとappy、callは少し違っています.bindメソッドは新しい関数を返して、後で呼び出して実行しますが、apply、callはすぐ実行します.
    二、Function.prototype.bind()
    bind()の方法は主に関数をあるオブジェクトに結びつけることで、bind()は関数を作成し、関数の体内のthisオブジェクトの値は、ビnd()に入ってくる最初のパラメータの値に結び付けられます.例えば、f.bind(obj)は、実際にはobj.f()と理解できます.この時、f関数の体内のthisは、自然にobjを指します.
    例:
    function f(y, z){
        return this.x + y + z;
    }
    var m = f.bind({x : 1}, 2);
    console.log(m(3));
    //6
    
    ここでビッド法はその最初の実証をf関数の体内のthisに結びつけるので、ここのthisは{x:1}オブジェクトを指し、2番目のパラメータから順に原始関数に伝えます.ここの2番目のパラメータ2、すなわちf関数のyパラメータです.最後にm(3)を呼び出すと、ここの3は最後のパラメータzです.したがって、実行結果は、1+2+3=6段階でパラメータを処理するプロセスは、典型的な関数コリック化のプロセスである.
    三、call/appy
    1.定義
    各関数は、引継ぎではない2つの方法を含む.
    callとappyは関数の実行環境、すなわちthisの方向を再定義するために使用できます.callとapplyはいずれも、ある関数が動作するときのcontext、すなわちコンテキストを変えるために存在します.言い換えれば、関数の内部thisの方向を変えるためです.
    2.文法
    コール
    オブジェクトを呼び出す方法は、現在のオブジェクトを別のオブジェクトに置き換え、別のオブジェクトの属性を継承することができます.
    Function.call(obj[, param1[, param2[, [,...paramN]]]]);
    
    このオブジェクトは、Functionクラスのthisオブジェクトparamsの代わりになります.
    説明:call方法は、他のオブジェクトの代わりに1つの方法を呼び出すために使用されてもよく、call方法は、関数のオブジェクトコンテキストを初期のコンテキストからObjで指定された新しいオブジェクトに変更してもよく、Objパラメータが提供されていない場合、Globalオブジェクトは、Objに使用される.
    appy()
    コール()方法と同じですが、パラメータリストだけが異なります.文法:
    Function.apply(obj[,argAray]);
    Obj:このオブジェクトはFunctionクラスのthisオブジェクトargArayの代わりになります.これは配列です.パラメータとしてFunctionに伝えます.
    説明:もしargArayが有効な配列ではないか、またはargmentsオブジェクトではないなら、Type Errorを招きます.argArayとobjのいずれかのパラメータが提供されていないなら、Globalオブジェクトはobjとして使用されます.
    3.異同
    同じ点
    call()とappy()の方法の同じ点はこの2つの方法の役割は同じです.いずれも特定の作用領域で関数を呼び出すもので、関数の内部のthisオブジェクトを設定する値に等しく、関数を拡張して実行する作用領域です.
    一般的には、thisは、ある方法を呼び出す対象を指しますが、call()とappy()の方法を使うと、thisの指向が変わります.例を見ます.
    function add(a, b) {
        return a + b;
    }
    
    function sub(a, b) {
        return a - b;
    }
    
    console.log(add.call(sub, 2, 1));//3
    
    なぜadd.call(sub,2,1)の実行結果は3なのか?call()の方法がthisの指向を変えたので、subがaddを呼び出す方法、つまりsubでaddの内容を実行して、もう一つの例を見てください.
    function People(name, age) {
        this.name = name;
        this.age = age;
    }
    
    function Student(name, age, grade) {
        People.call(this, name, age);
        this.grade = grade;
    }
    
    var student = new Student('  ', 21, '  ');
    console.log(student.name + student.age + student.grade);//  21  
    
    この例では、Studentのnameとageに値を割り当てていませんが、この2つの属性の値が存在します.これはやはりコール方法のおかげで、thisの方向を変えることができます.この例では、People.call(this,name,age);のthisはStudentを表しています.つまり、StudentはPeopleの方法を呼び出すことができます.Peopleにはthis.name=nameがあります.などの語句があり、これによりnameとageの属性をStudentに作成しました.
    まとめると、括弧内のオブジェクトに括弧外の関数の属性を引き継ぐことができます.
    apply()方法の役割もcall()方法と同じです.
    People.apply(this, [name, age]);
    
    あるいはこのように書きます
    People.apply(this, arguments);
    
    ここでargmentsとname、ageは等価です.
    違い点
    定義からもわかるように、call()とappy()の違いは、受信パラメータの方式が違っています.
    1.apply()方法は2つのパラメータを受信します.一つは関数動作のスコープ、もう一つはパラメータ配列です.2.call()メソッドは必ずしも二つのパラメータを受け入れていません.最初のパラメータも関数動作のスコープですが、関数に渡すパラメータは列挙しなければなりません.
    オブジェクトパラメータの場合、パラメータの形式が配列の場合、例えば、以前のapply()方法例でパラメータargmentsが渡されていますが、このパラメータは配列タイプであり、Personを呼び出したときのパラメータのリストは対応一致しています(つまりPersonとStudentのパラメータリストの前の2桁が一致しています).
    しかし、Personのパラメータリストがこのような(age,name)なら、Studentのパラメータリストは(name,age,grade)であり、これはcall()方法で実現でき、すなわちパラメータリストの対応値を直接指定する位置Person.call(this,age,name)である.