[TIL]モダンJavaScript Deep Dive-this


第22章.this
このキーワード
「オブジェクト向けプログラミング」で見たように、オブジェクトは状態を表すプログラムと動作(動作)を表す方法を論理ユニットに組み合わせた複合材料構造である.
動作を表す方法は、その属するオブジェクトの状態、すなわちPropertyを参照して変更できる必要があります.メソッドが属するオブジェクトのプロパティを参照する場合は、まずそのオブジェクトを指す識別子を参照する必要があります.
オブジェクトテキストで作成されたオブジェクトについては、メソッド自体が属するオブジェクトを指す識別子をメソッド内部で再帰的に参照できます.
const circle = {
  // 프로퍼티: 객체 고유의 상태 데이터
  radius: 5,
  // 메서드: 상태 데이터를 참조하고 조작하는 동작
  getDiameter() {
    // 이 메서드가 자신이 속한 객체의 프로퍼티나 다른 메서드를 참조하려면
    // 자신이 속한 객체인 circle을 참조할 수 있어야 한다.
    return 2 * circle.radius;
  }
};

console.log(circle.getDiameter()); // 10
上記の例のオブジェクト文字は、ループ変数に割り当てる前に評価されます.したがって、getDiameterメソッドを呼び出すと、オブジェクト文字の計算が完了し、オブジェクトが作成され、作成されたオブジェクトが円識別子で指定された後になります.したがって、ループ識別子はメソッド内で参照できます.
しかし、自分の属するオブジェクトを再引用する方法は一般的ではなく、望ましくない.関数を構築する方法でインスタンスを作成することを考慮します.
function Circle(radius) {
  // 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가리키는 식별자를 알 수 없다.
  ????.radius = radius;
}

Circle.prototype.getDiameter = function () {
  // 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가리키는 식별자를 알 수 없다.
  return 2 * ????.radius;
};

// 생성자 함수로 인스턴스를 생성하려면 먼저 생성자 함수를 정의해야 한다.
const circle = new Circle(5);
コンストラクション関数の内部では、作成するインスタンスを参照してPropertyまたはメソッドを追加する必要があります.ただし、コンストラクション関数に基づいてオブジェクトを作成する方法では、コンストラクション関数を定義した後、new演算子とともにコンストラクション関数を呼び出す必要があります.すなわち、コンストラクション関数でインスタンスを作成するには、まずコンストラクション関数が存在する必要があります.
コンストラクション関数を定義する時間がインスタンスの作成よりも早いため、コンストラクション関数が作成するインスタンスを指す識別子は決定できません.したがって、所属するオブジェクトまたは作成するインスタンスを示す特別な識別子が必要です.このため、JavaScriptにはthisという特殊な識別子が用意されています.
これは、所属するオブジェクトまたは作成するインスタンスを指す自己参照変数です.これにより、オブジェクトまたは作成するインスタンスに属するプロシージャまたはメソッドを参照できます.
これはJavaScriptエンジンによって暗黙的に生成され、コードはどこでも参照できます.関数を呼び出すとargumentsオブジェクトとそれは黙って関数の内部に渡されます.関数内でargumentsオブジェクトを領域変数として使用できるように、領域変数としても使用できます.しかし、thisが指す値、すなわちthisバインドは関数呼び出し方式によって動的に決定される.
このバインディング
バインドは、識別子と値を関連付けるプロセスを意味します.たとえば、変数宣言は、変数名(識別子)と保存済みメモリ領域をバインドするアドレスです.このバインドは、this(キーワードに分類されますが、識別子として機能します)とthisが指すオブジェクトをバインドします.
上で説明したオブジェクト文字とコンストラクション関数の例をthisを使用して変更します.
// 객체 리터럴
const Circle = {
  radius: 5,
  getDiameter() {
    // this는 메서드를 호출한 객체를 가리킨다.
    return 2 * this.radius;
  }
};

console.log(circle.getDiameter()); // 10
オブジェクト文字のメソッドでは、thisはメソッドを呼び出すオブジェクト、すなわち円を指します.
// 생성자 함수
function Circle(radius) {
  // this는 생성자 함수가 생성할 인스턴스를 가리킨다.
  this.radius = radius;
}

Circle.prototype.getDiameter = function() {
  // this는 생성자 함수가 생성할 인스턴스를 가리킨다.
  return 2 * this.radius;
};

// 인스턴스 생성
const circle = new Circle(5);
console.log(circle.getDiameter()); // 10
コンストラクション関数の内部にあるthisは、コンストラクション関数が作成するインスタンスを指します.このように、thisは状況によって指す対象が異なります.
JavaScriptのthisは、関数呼び出しに基づいてthisにバインドされる値、すなわちthisバインドを動的に決定します.また、strict modeもこのバインドに影響します.
これはコードのどこでも参考になります.グローバルでは、関数の内部参照もできます.
// this는 어디서든지 참조 가능하다.
// 전역에서 this는 전역 객체 window를 가리킨다.
console.log(this); // window

function square(number) {
  // 일반 함수 내부에서 this는 전역 객체 window를 가리킨다.
  console.log(this); // window
  return number * number;
}
square(2);

const person = {
  name: 'Lee',
  getName() {
    // 메서드 내부에서 this는 메서드를 호출한 객체를 가리킨다.
    console.log(this); // {name: "Lee", getName: f}
    return this.name;
  }
}
console.log(person.getName()); // Lee

function Person(name) {
  this.name = name;
  // 생성자 함수 내부에서 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
  console.log(this); // Person {name: "Lee"}
}

const me = new Person('Lee');
しかし、これはオブジェクトを参照するpropertyまたはメソッドの自己参照変数であり、通常はオブジェクトのメソッド内部またはコンストラクション関数内部にのみ意味があります.従ってstrict modeを適用した一般的な関数の内部では,thisはundefinedをバインドした.一般的な関数の内部ではthisを使用する必要はありません.
関数呼び出しとバインド
このバインド(この値にバインドされる)は、関数が呼び出される方法(すなわち、関数がどのように呼び出されるか)によって動的に決定されます.
同じ関数を複数の方法で呼び出すこともできることに注意してください.関数を呼び出す方法は次のとおりです.

  • 一般関数の呼び出し

  • メソッド呼び出し

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

  • Function.prototype.apply/call/bindメソッドによる間接呼び出し
  • このバインドが関数の呼び出し方法に依存する方法について説明します.
    一般関数の呼び出し
    デフォルトでは、thisはグローバルオブジェクトをバインドします.
    function foo() {
      console.log("foo's this: ", this); // window
      function bar() {
        console.log("bar's this: ", this); // window
      }
      bar();
    }
    foo();
    上記の例に示すように、ネストされた関数を通常の関数として呼び出すと、関数内部のthisはグローバルオブジェクトをバインドします.ただし、これはオブジェクトを参照するためのPropertyまたはメソッドの自己参照変数であるため、オブジェクトを作成しない通常の関数では意味がありません.従ってstrict modeを適用した一般的な関数の内部では,thisはundefinedをバインドした.
    メソッドで定義されたネスト関数も通常の関数として呼び出されると、ネスト関数のthisはグローバルオブジェクトをバインドします.
    // var 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티다.
    var value = 1;
    // const 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티가 아니다.
    // const value = 1;
    
    const obj = {
      value: 100,
      foo() {
        console.log("foo's this: ", this); // {value: 100, foo: f}
        console.log("foo's this.value: ", this.value); // 100
        
        // 메서드 내에서 정의한 중첩 함수
        function bar() {
          console.log("bar's this: ", this); // window
          console.log("bar's this.value: ", this.value); // 1
        }
        
        // 메서드 내에서 정의한 중첩 함수도 일반 함수로 호출되면 중첩 함수 내부의 this에는
        // 전역 객체가 바인딩된다.
        bar();
      }
    };
    
    obj.foo();
    コールバック関数が通常の関数として呼び出されると、コールバック関数内部のthisもグローバルオブジェクトをバインドします.通常の関数として関数を呼び出すと、thisでグローバルオブジェクトがバインドされます.
    このように、ネスト関数とコールバック関数を含む通常の関数で呼び出されるすべての関数の内部にあるthisは、グローバルオブジェクトをバインドします.
    しかし、メソッドで定義されたネスト関数またはメソッドに渡されるコールバック関数(補助関数)が通常の関数として呼び出されると、メソッド内のネスト関数またはコールバック関数のthisがグローバルオブジェクトをバインドすることが問題になります.ネスト関数またはコールバック関数は、外部関数のhelper関数を支援する役割を果たすため、外部関数のいくつかの論理に代わることが多い.しかし,外部関数法はネスト関数やコールバック関数のthisと一致せず,ネスト関数やコールバック関数をhelper関数として操作することが困難である.
    メソッド内のネスト関数またはコールバック関数のthisバインドとメソッドのthisバインドを一致させる方法は、次のとおりです.
    var value = 1;
    
    const obj = {
      value: 100,
      foo() {
        // this 바인딩(obj)을 변수 that에 할당한다.
        const that = this;
        
        // 콜백 함수 내부에서 this 대신 that을 참조한다.
        setTimeout(function () {
          console.log(that.value); // 100
        }, 100);
      }
    };
    
    obj.foo();
    矢印関数を使用して、バインドを一致させることもできます.
    var value = 1;
    
    const obj = {
      value: 100,
      foo() {
        // 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.
        setTimeout(() => console.log(this.value), 100); // 100
      }
    };
    
    obj.foo();
    メソッド呼び出し
    メソッド内部のthisは、メソッドを呼び出すオブジェクト、すなわちメソッドを呼び出すときのメソッド名の前のピリオド(.)先に説明したオブジェクトをバインドします.メソッドが持つオブジェクトではなく、メソッド内部のthisがメソッドを呼び出すオブジェクトにバインドされていることに注意してください.
    const person = {
      name: 'Kyeom',
      getName() {
        // 메서드 내부의 this는 메서드를 호출한 객체에 바인딩된다.
        return this.name;
      }
    };
    
    // 메서드 getName을 호출한 객체는 person이다.
    console.log(person.getName()); // Kyeom
    上記の例のgetNameメソッドは、personオブジェクトのメソッドとして定義されています.方法はプログラムにバインドされた関数です.すなわち,personオブジェクトのgetName Propertyが指す関数オブジェクトは,personオブジェクトに含まれるのではなく,独立して存在する独立したオブジェクトである.getName Propertyは、関数オブジェクトのみを指します.
    したがって、getName Propertyが指す関数オブジェクト、すなわちgetNameメソッドは、他のオブジェクトに割り当てられたPropertyのメソッドであってもよいし、一般変数に割り当てられて一般関数として呼び出されるメソッドであってもよい.
    const anotherPerson = {
      name: 'Kim'
    };
    // getName 메서드를 anotherPerson 객체의 메서드로 할당
    anotherPerson.getName = person.getName;
    
    // getName 메서드를 호출한 객체는 anotherPerson이다.
    console.log(anotherPerson.getName()); // Kim
    
    // getName 메서드를 변수에 할당
    const getName = person.getName;
    
    // getName 메서드를 일반 함수로 호출
    console.log(getName()); // ''
    //일반 함수로 호출된 getName 함수 내부의 this.name은 브라우저 환경에서 window.name과 같다.
    // 브라우저 환경에서 window.name은 브라우저 창의 이름을 나타내는 빌트인 프로퍼티이며 기본값은 ''이다.
    // Node.js 환경에서 this.name은 undefined다.
    したがって,メソッド内部のthisはメソッドを指すオブジェクトとは無関係に,メソッドを呼び出すオブジェクトにバインドされる.
    プロトタイプメソッドで使用されるthisは、通常のメソッドと同様に、メソッドを呼び出すオブジェクトにバインドされます.
    コンストラクタの呼び出し
    コンストラクション関数の内部にあるthisでは、コンストラクション関数は作成するインスタンスをバインドします.
    // 생성자 함수
    function Circle(radius) {
      // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
      this.radius = radius;
      this.getDiameter = function() {
        return 2 * this.radius;
      };
    }
    
    const circle1 = new Circle(5);
    const circle2 = new Circle(10);
    
    console.log(circle1.getDiameter()); // 10
    console.log(circle2.getDiameter()); // 20
    コンストラクション関数は、オブジェクト(インスタンス)を名前で作成する関数です.通常の関数と同じ方法でコンストラクション関数を定義し、new演算子とともに呼び出すと、コンストラクション関数として動作します.new演算子とともにコンストラクション関数を呼び出さない場合は、コンストラクション関数ではなく通常の関数として使用されます.
    // new 연산자와 함께 호출하지 않으면 생성자 함수로 동작하지 않는다. 즉, 일반적인 함수의 호출이다.
    const circle3 = Circle(15);
    
    // 일반 함수로 호출된 Circle에는 반환문이 없으므로 암묵적으로 undefined를 반환한다.
    console.log(circle3); // undefined
    
    // 일반 함수로 호출된 Circle 내부의 this는 전역 객체를 가리킨다.
    console.log(raidus); // 15
    これまで,関数呼び出し方式に基づいてthisバインドを動的に決定することを議論した.以下に整理します.
    関数呼び出し方式thisは、通常の関数呼び出しグローバルオブジェクトメソッド呼び出し方法のオブジェクトジェネレータ関数番号生成構造関数が作成するインスタンス機能をバインドします.prototype.Functionはapply/call/bindメソッドで間接的に呼び出されます.prototype.apply/call/bindメソッドに最初の引数として渡されるオブジェクト