JS07-2 this


このシリーズの文章はモダンJavaScript Deep DiveBookによって書かれています.

これは何ですか。


オブジェクトが所有する過程で、その値はメソッドと呼ばれる関数です.メソッドは、オブジェクトが持つ状態(Property)を参照または変更するためにオブジェクトの動作を表します.メソッドが属するオブジェクトには、オブジェクト自体を指す識別子が必要です.thisは、自分が属するオブジェクトまたは作成するインスタンスを指す自己参照変数です.
このという別の自己参照変数が必要なのは、通常、オブジェクトを作成するときに、作成するインスタンスを指す識別子が分からないためです.特にコンストラクション関数では、インスタンスを作成する前にコンストラクション関数を作成する必要があるため、どの識別子がインスタンスを指すか分かりません.したがって、これによって作成されるインスタンスを指します.
関数が呼び出されると、thisオブジェクトとargumentsオブジェクトが関数内部に暗黙的に渡されます.したがって、領域変数のようにthisとargumentsを使用することができ、処理を必要としません.関数が呼び出されたときにthisが渡されるため、関数の呼び出し方式がthisのバインド方式を決定します.それ以外にstrict modeもこのバインドに影響します.
なお、JavaやC++などのクラスベースのオブジェクト向けプログラミング言語では、thisは常にクラス生成のインスタンスを指します.

関数呼び出しとバインド


関数を呼び出す方法は次のとおりです.
  • 呼び出し汎用関数
  • 呼び出し方法
  • 呼び出しコンストラクタ
  • Function.prototype.apply/call/bindメソッドによる間接呼び出し
  • メソッド呼び出し


    メソッド呼び出し時に、ピリオド演算子の前に記述されたオブジェクトがthisにバインドされます.句点演算子の前のオブジェクトに呼び出す方法がない場合は、thisをオブジェクトにバインドします.すなわち,メソッドのthisは呼び出したオブジェクトにバインドされる.(下図参照)
    const objA = {
      name: "objA",
      x() {
        console.log(this.name);
      }
    }
    
    objA.x(); // objA
    
    const objB = {
      name: "objB"
    }
    
    // objB 객체에 objA.x 함수 객체를 프로퍼티로 동적 추가
    objB.x = objA.x; 
    // objB.x는 objA가 가지고 있는 메서드이지만, objB 객체를 this에 바인딩하여 메서드를 실행한다
    objB.x(); // objB

    コンストラクタの呼び出し


    ジェネレータ関数は、将来作成するインスタンスをここにバインドします.
    function Cat(name) {
      this.name = name;
      this.sayHi = function() {
        console.log("hi. I'm " + this.name);
      }
    
      this.sayHi();
    }
    
    const chunsik = new Cat("chunsik");

    汎用関数の呼び出し

    this === globalThis厳格モードでthis === undefined
    function foo() {
    	console.log("foo", this);
    	function bar() {
    		console.log("bar", this);
    	}
    	bar();
    }
    
    foo();
    
    const obj = {
    	x() {
    		setTimeout(function() {
    			console.log("callback", this);
    		}, 1000);
    	},
    	y() {
    		console.log("method", this);
    		function z() {
    			console.log("메소드 중첩", this);
    		}
    		z();
    	}
    };
    obj.x();
    obj.y();
    
    const method = obj.y
    method();
  • foo→一般関数:global
  • bar→通常関数のネスト関数:global
  • obj.x→通常の関数形式のコールバック関数:global
  • obj.y→オブジェクトによる呼び出し方法:objオブジェクト
  • z→オブジェクトメソッド内部のネスト関数:global
  • method→オブジェクトを通常の関数として呼び出す方法:global
  • この場合、メソッドの内部にネストされた関数呼び出しまたはコールバック関数は、グローバルオブジェクトではなく、より自然に自分の属するオブジェクトになります.この問題を解決する方法は以下の通りである.
  • 親関数にバインドされたオブジェクトを割り当てる変数
  • Function.prototype.bind明示的バインドを使用
  • 矢印関数を使用
  • const obj1 = {
    	x() {
    		const that = this;
    		setTimeout(function() {
    			console.log("callback", that);
    		}, 1000);
    	}
    };
    
    const obj2 = {
    	x() {
    		setTimeout(function() {
    			console.log("callback", this);
    		}.bind(this), 1000);
    	}
    };
    
    const obj3 = {
    	x() {
    		setTimeout(() => {
    			console.log("callback", this);
        }, 1000);
    	}
    };
    
    obj1.x();
    obj2.x();
    obj3.x();

    すべての人がthisを自分の属するオブジェクトにバインドします.

    Function.prototype.apply/call/bindメソッドによる間接呼び出し


    これらはFunction.prototypeのメソッドであり、すべての関数が継承されて使用できます.
    applyメソッドとcallメソッドは、この目的として使用するオブジェクトとパラメータのリストを受信することによって関数を呼び出します.applyは関数のパラメータを配列形式で伝達し,callはカンマで区切られたリスト形式でパラメータを伝達する.この2つの方法の本質的な機能は実行関数である.
    function add(a, b) {
      console.log(this.name);
      return a + b;
    }
    
    const obj = {
      name: "obj",
      a: 12,
      b: 45
    }
    
    add.apply(obj, [obj.a, obj.b]); // obj, 57
    add.call(obj, obj.a, obj.b); // obj, 57
    bindメソッドは、このメソッドを使用するオブジェクトのみを渡し、関数にバインドします.関数は呼び出されません.この方法は、ネストされた関数またはコールバック関数のthisをグローバルオブジェクトにバインドするために使用できます.

    整理する


    関数呼び出しメソッドこのバインド汎用関数グローバルオブジェクトメソッド呼び出しメソッドのオブジェクトジェネレータ関数将来作成するインスタンスオブジェクト関数を呼び出します.prototype.apply/call/bindの最初の引数として渡されるオブジェクト