[딥다이브] this

yunji_kim·2023년 3월 30일
0
post-thumbnail

해당 포스팅은 위키북스의 '모던 자바스크립트 Deep Dive'를 읽고 공부한 개념을 정리한 글입니다.

22장 this

this 키워드

this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수다.

this는 자바스크립트 엔진에 의해 암묵적으로 생성되며 코드 어디서든 참조할 수 있다. 함수를 호출하면 arguments 객체와 this가 암묵적으로 함수 내부에 전달되며 지역 변수처럼 사용할 수 있다.

단, this가 가리키는 값, 즉 this 바인딩은 함수 호출 방식에 의해 동적으로 결정된다.

함수 호출 방식과 this 바인딩

1. 일반 함수 호출

전역 함수, 중첩 함수, 콜백 함수를 일반 함수로 호출하면 함수 내부의 this에는 전역 객체가 바인딩된다. 다만 객체를 생성하지 않는 일반 함수에서는 this는 의미가 없다.

function foo() {
  console.log("foo's this: ", this);  // window
  function bar() {
    console.log("bar's this: ", this); // window
  }
  bar();
}
foo();

strict mode가 적용된 일반 함수의 경우 this는 undefined가 바인딩된다.

function foo() {
  'use strict';

  console.log("foo's this: ", this);  // undefined
  function bar() {
    console.log("bar's this: ", this); // undefined
  }
  bar();
}
foo();

2. 메서드 호출

메서드 내부의 this에는 메서드를 호출한 객체가 바인딩된다.

주의할 것은 메서드 내부의 this는 메서드를 소유한 객체가 아닌, 메서드를 호출한 객체에 바인딩된다는 것이다.

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가 바인딩된다.

3. 생성자 함수 호출

생성자 함수 내부의 this에는 생성자 함수가 미래에 생성할 인스턴스가 바인딩된다.

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

// 반지름이 5인 Circle 객체를 생성
const circle1 = new Circle(5);
// 반지름이 10인 Circle 객체를 생성
const circle2 = new Circle(10);

console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20

4. Function.prototype.apply/call/bind 간접 호출

  1. apply와 call

apply와 call 메서드의 본질적인 기능은 함수를 호출하는 것이다. 둘은 호출할 함수에 인수를 전달하는 방식만 다를 뿐 동일하게 동작한다.

apply와 call 메서드는 함수를 호출하면서 첫 번째 인수로 전달한 특정 객체(thisArg)를 호출한 함수의 this에 바인딩한다.

// apply
myFunc.apply(thisArg[, argsArray])
// call
myFunc.call(thisArg[, arg1[, arg2[, ...]]])
  • thisArg : this로 사용할 객체
  • argsArray(optional) : 함수에게 전달할 인수 리스트의 배열 또는 유사 배열 객체
  • arg1, arg2, … : 함수에게 전달할 인수 리스트

<apply, call 예제 코드>

function getThisBinding() {
  console.log(arguments);
  return this;
}

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

// getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩한다.
// apply
console.log(getThisBinding.apply(thisArg, [1, 2, 3]));
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// {a: 1}

// call
console.log(getThisBinding.call(thisArg, 1, 2, 3));
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// {a: 1}
  1. bind

apply, call과 달리 함수를 호출하지 않는다. 다만 첫 번째 인수로 전달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환한다.

bind 메서드는 메서드의 this와 메서드 내부의 중첩 함수 또는 콜백 함수의 this가 불일치하는 문제를 해결하기 위해 유용하게 사용된다.

myFunc.bind(thisArg)

<bind의 this 불일치 문제 해결 예제 코드>

const person = {
  name: 'Lee',
  foo(callback) {
    setTimeout(callback, 100);
  }
};

person.foo(function () {
  console.log(`Hi! my name is ${this.name}.`); // ② Hi! my name is .
  // 일반 함수로 호출된 콜백 함수 내부의 this.name은 브라우저 환경에서 window.name과 같다.
  // 브라우저 환경에서 window.name은 브라우저 창의 이름을 나타내는 빌트인 프로퍼티이며 기본값은 ''이다.
  // Node.js 환경에서 this.name은 undefined다.
});
const person = {
  name: 'Lee',
  foo(callback) {
    // bind 메서드로 callback 함수 내부의 this 바인딩을 전달
    **setTimeout(callback.bind(this), 100);**
  }
};

person.foo(function () {
  console.log(`Hi! my name is ${this.name}.`); // Hi! my name is Lee.
});
profile
| FE Developer | 기록의 힘 |

0개의 댓글