본문 바로가기
[Study] Deep Dive 스터디

[JS] this

by 지공A 2024. 1. 13.

22장 : this

1. this 키워드

  • this : 자신이 속한 객체 또는 생성할 인스턴스를 가리키는 자기 참조 변수
  • this를 통해 자신이 속한 객체 또는 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.
const circle = {
  radius: 5, // property
  getDiameter() {
    // method
    // 이 method가 자신이 속한 객체의 property인 radius를 참조하려면
    // 자신이 속한 객체인 circle을 참조할 수 있어야 한다.
    return 2 * circle.radius;
  },
};
console.log(circle.getDiameter()); // 10
  • circle.radius 과 같이 자기 자신이 속한 객체를 재귀적으로 참조하는 방식은 일반적이지 않으며 바람직하지도 않음!
  • this 를 사용하여 수정해보자!
const circle = {
  radius: 5,
  getDiameter() {
    // this는 메서드를 호출한 객체인 circle을 가리킨다.
    return 2 * this.radius;
  },
};
console.log(circle.getDiameter()); // 10
  • this.radius : 객체 리터럴의 메서드 내부에서의 this는 메서드를 호출한 객체(circle)를 가리킨다.  

2. 함수 호출 방식과 this 바인딩

  • this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다.
    1. 일반 함수 호출
    2. 메서드 호출
    3. 생성자 함수 호출
    4. applay/call/bind 에 의한 간접 호출

 1. 일반 함수 호출

  • 일반 함수로 호출 시 함수 내부의 this에는 전역 객체가 바인딩된다.
function foo() {
  console.log("foo's this: ", this); // window
  function bar() {
    console.log("bar's this: ", this); //window
  }
  bar();
}
foo();

 

  • 중첩 함수, 콜백 함수를 포함한 일반 함수로 호출된 모든 함수 내부의 this에는 전역 객체가 바인딩된다.

  ⭐ 메서드 내부의 중첩 함수나 콜백 함수의 this 바인딩을 메서드의 this 바인딩과 일치시키기 위한 방법

  • 변수에 this를 할당하기
var value = 1;

const obj = {
  value: 100,
  foo() {
    // this 바인딩(obj)을 변수 that에 할당한다.
    const that = this;
    // 일반 함수로 호출된 콜백 함수 내부의 this에는 전역 객체가 바인딩
    setTimeout(function () {
      console.log(this.value); // 1
      console.log(that.value); // 100
    }, 100);
  },
};

 

  • apply / call / bind 메서드를 사용한 명시적 바인딩
var value = 1;

const obj = {
  value: 100,
  foo() {
    setTimeout(
      function () {
        console.log(this.value); // 100
      }.bind(this),
      100
    );
  },
};
  • 화살표 함수 사용
var value = 1;

const obj = {
  value: 100,
  foo() {
    // 화살표 함수 내부의 this는 상위 스코프의 this(obj)를 가리킨다.
    setTimeout(() => console.log(this.value), 100); // 100
  },
};

 2. 메서드 호출

  • 메서드 내부의 this메서드를 호출한 객체에 바인딩 된다.
  • 메서드를 소유한 객체가 아님에 주의!
const person = {
  name: "Lee",
  getName() {
    return this.name;
  },
};

console.log(person.getName()); // Lee

const anotherPerson = {
  name: "Kim",
};

// getName 메서드를 anotherPerson 객체의 메서드로 할당
anotherPerson.getName = person.getName;

// getName 메서드를 호출한 객체는 anotherPerson
console.log(anotherPerson.getName()); // Kim

// getName 메서드를 변수에 할당
const getName = person.getName;

// getName 메서드를 일반 함수로 호출
// 일반 함수로 호출된 getName 함수 내부의 this.name의 this는 전역 객체
console.log(getName()); // ''
  • person.getName() : getName 을 호출한 객체가 person 이기 때문에 Lee 가 출력된다.
  • getName() : 일반 함수로 호출 시 함수 내부 this(return this.name)에는 전역 객체가 binding 되므로 window.name의 값이 출력된다.

 3. 생성자 함수 호출

  • 생성자 함수 : 객체(인스턴스)를 생성하는 함수. new 연산자와 함께 호출하지 않으면 일반 함수로 동작한다.
  • 생성자 함수 내부의 this에는 생성자 함수가 생성할 인스턴스가 바인딩된다.
function Circle(radius) {
  // 생성자 함수 내부의 this는 생성자 함수가 생성할 객체(인스턴스)를 가리킨다
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };
}

// radius = 5 인 Circle 객체 circle1 생성
const circle1 = new Circle(5);
console.log(circle1.getDiameter()); // 10

// new 연산자와 함께 호출하지 않아 일반 함수로 호출
const circle2 = Circle(15);

// 일반 함수 Circle은 return이 없으므로
console.log(circle2); // undefined

// 일반 함수로 호출된 Circle 내부의 this는 전역 객체를 가리킨다.
console.log(radius); // 15
  • circle1.getDiameter() : new Circle(5) 내부의 thiscircle1 객체를 가리킨다.
  • const circle2 = Circle(15) : 일반 함수로 호출된 Circle 내부의 this는 전역 객체를 가리킨다. 
  • console.log(radius) : 따라서 window.radius 의 값이 15가 된다. 

 4. apply / call / bind 메서드에 의한 간접 호출

  • 해당 메서드는 Function.prototype의 메서드이므로 모든 함수가 상속받아 사용할 수 있다.
  • 각 함수의 사용법 알아보기
  • apply call 메서드는 함수를 호출하면서 첫 번째 인수로 전달한 특정 객체를 호출한 함수의 this에 바인딩한다.
  • 두 메서드는 인수를 전달하는 방식만 다를 뿐 동일하게 동작한다.
    • apply : 호출할 함수의 인수를 배열 하나로 전달한다. (ex. [1, 2, 3])
    • call : 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다. (ex. 1, 2, 3)
  • 두 메서드는 주로 arguments 객체(함수에 전달된 인수의 배열 형태의 객체)에 배열 메서드를 사용하기 위해 사용한다.
function getThisBinding() {
  console.log(arguments);
  return this;
}

// this로 사용할 객체
const thisArg = { a: 1 };

// getThisBinding 함수를 호출하면서 인수로 전달할 객체를 해당 함수의 this에 바인딩
console.log(getThisBinding.apply(thisArg, [1, 2, 3]));
console.log(getThisBinding.call(thisArg, 1, 2, 3));
// Arguments(3) [1, 2, 3, callee: f, Symbol(Symbol.iterator): f]
// { a: 1 }
  • bind 메서드는 첫 번째 인수로 전달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환한다.
  • bind 메서드는 메서드의 this와 메서드 내부의 중첩 함수 또는 콜백 함수의 this가 불일치할 때 유용하게 사용된다.
  • 아래 예제는 콜백 함수가 일반 함수로서 호출되고 있다.
const person = {
  name: "Lee",
  foo(cb) {
    // 현재 this는 foo 메서드를 호출한 person 객체
    setTimeout(cb, 100);
  },
};

person.foo(function () {
  // 현재 this는 일반 함수로서 호출되었으므로 전역 객체
  console.log(`my name is ${this.name}.`); // my name is .
});
  • 위 예제를 bind 메서드를 사용하여 this의 바인딩을 교체할 수 있다.
const person = {
  name: "Lee",
  foo(cb) {
    // bind 메서드로 콜백 함수의 this가 현재 시점의 this(person)로 바인딩된다.
    setTimeout(cb.bind(this), 100);
  },
};

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

Deep Dive Study week - 01

'[Study] Deep Dive 스터디' 카테고리의 다른 글

[JS] 이터러블  (0) 2024.01.23
[JS] 프로미스  (0) 2024.01.22
[JS] 비동기 프로그래밍  (0) 2024.01.20
[JS] ES6 함수의 추가 기능  (0) 2024.01.16
[JS] 클로저  (0) 2024.01.14