[딥다이브] 22. this

codeyun2 2024. 6. 9. 23:37

22.1 this 키워드

객체 리터럴 방식으로 생성한 객체: 메서드 내부에서 메서드 자신이 속한 객체를 가리키는 식별자를 재귀적으로 참조

const circle = {
  radius: 5,
  getDiameter() {
    return 2 * circle.radius;
  }
};

getDiameter의 circle 참조 표현식이 평가되는 시점: getDiameter 메서드가 호출되어 함수 몸체가 실행되는 시점

 

미래에 생성될 인스턴스의 식별자를 알 수 없기 때문에 재귀적으로 참조 방식은 일반적이지 않고 바람직하지 않음

=> 자신이 속한 객체 / 자신이 생성할 인스턴스를 가리키는 특수한 식별자 this(자기 참조 변수) 사용

자바스크립트 엔진에 의해 암묵적으로 생성되며 코드 어디서든 참조할 수 있음

함수 호출 시 arguments 객체와 this가 암묵적으로 함수 내부에 전달됨

 

전역에서 this: window

일반 함수에서 this: window(엄격모드에서는 undefined 일반 함수 내부에서 this로 자기 참조할 필요가 없기 때문)

객체 메서드 내부에서 this: 메서드를 호출한 객체

생성자 함수 내부에서 this: 생성자 함수가 생성할 인스턴스

 

자바나 C++ 같은 클래스 기반 언어에서 this는 항상 클래스가 생성하는 인스턴스를 가리킴

 

22.2 함수 호출 방식과 this 바인딩

렉시컬 스코프: 함수 정의가 평가되어 함수 객체가 생성되는 시점에 상위 스코프를 결정

this 바인딩: 함수 호출 시점에 결정

 

함수 호출 방식

const foo = function () {
  console.dir(this);
};

foo(); // window

const obj = { foo };
obj.foo(); // obj

new foo(); // foo {}

const bar = { name: 'bar'};
foo.call(bar); // bar
foo.apply(bar); // bar
foo.bind(bar)(); // bar
  1. 일반 함수 호출: window(전역 객체)
    어떤 함수라도 일반 함수로 호출되면 this에 전역 객체가 바인딩됨
    메서드 내부에서 정의한 중첩 함수, 콜백 함수 등도 그러한데 이들은 외부 함수(메서드)를 돕는 헬퍼 함수의 역할을 하는 경우가 대부분이라 this 바인딩을 일치시키기 위한 작업이 필요함
    const obj = {
      value: 100,
      foo() {
        const that = this;
        
        setTimeout(function () {
          console.log(that.value);
        }, 100);
     };
     
     // Function.prototype.apply/call/bind 메서드 사용
     const obj = {
      value: 100,
      foo() {    
        setTimeout(function () {
          console.log(this.value);
        }.bind(this), 100);
     }; 
     
    // 화살표 함수 이용
    const obj = {
      value: 100,
      foo() {    
        setTimeout(() => console.log(that.value), 100);
      }
    };
  2. 메서드 호출: this는 메서드를 호출한 객체를 가리킴
    메서드는 프로퍼티에 바인딩(할당)된 함수이기 때문에 다른 객체의 프로퍼티에 할당하거나 일반 변수에 할당하여 사용할 수 있음(프로토타입 메서드도 동일) -> 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩됨
  3. 생성자 함수 호출: 생성자 함수가 생성할 인스턴스를 가리킴

4. Function.prototype.apply/call/bind 메서드에 의한 간접 호출: 첫 번째 인수로 전달된 객체(this)에 의해 결정

Function.prototype.apply(thisArg[, argsArray])
Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])

- Function.prototype.apply, Function.prototype.call

인수 전달 방식만 다를 뿐 동일하게 동작

함수를 호출하면서 첫 번째 인수로 전달한 특정 객체를 호출한 함수의 this에 바인딩함

주 용도: arguments 객체같은 유사 배열 객체에 배열 메서드를 사용하는 경우에 사용

function convertArgsToArray() {
  const arr = Array.prototype.slice.call(arguments);
  console.log(arguments, arr);
  return arr;
}

convertArgsToArray(1, 2, 3);
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// [1, 2, 3]

 

- Function.prototype.bind

인수로 전달한 값으로 this 바인딩이 교체된 새로운 함수 반환

메서드의 this와 메서드 내부의 중첩 함수, 콜백 함수의 this가 불일치하는 문제(22.2.2)를 해결하기 위해 유용하게 사용됨

// 콜백 함수의 this와 외부 함수의 this를 일치시키기
const person = {
  name: 'Lee',
  foo(callback) {
    setTimeout(callback.bind(this), 100);
  }
};

person.foo(function () {
  console.log(`Hi! my name is ${this.name}.`);
});