본문 바로가기
자바스크립트/코어 자바스크립트

코어자바스크립트 3장<this>

by SONBAEJUN 2022. 8. 13.

1. 상황에 따라 달라지는 this

  • 스크립트에서 this는 기본적으로 실행 컨텍스트가 생성될 때 함께 결정된다.
  • 실행 컨텍스트는 함수를 호출할 때 생성되므로, 바꿔 말하면 this는 함수를 호출할 때 결정된다.

1-1) 전역 공간에서의 this

  • 전역 공간에서 this는 전역 객체를 가리킨다.
  • 전역 객체는 JS 런타임 환경에 따라 다른 이름과 정보를 가진다(브라우저=window, Node.js=global)

※전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로도 할당한다!?

  • 전역공간에서 변수 a에 1을 할당하니 window.a(전역객체)와 this.a 모두 1로 출력된다.
  • 이는 '전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당한다'라는 공식 때문이다.
  • 따라서 window(전역객체)의 프로퍼티로도 a가 할당되므로 window.a 가 1이 되는것이다.
  • 이 말은 즉슨, window의 프로퍼티에 직접 할당하더라도 결과적으로 var로 선언한 것과 같이 동작한다 (단, 삭제 명령 시에는 동일하게 동작하지 않는데, 사용자의 의도치않은 삭제를 방지하는 차원에서 마련한 방어책이라고 한다)

1-2) 메서드로서 호출할 때 그 메서드 내부에서의 this

  • 어떤 함수를 실행하는 방법 중 일반적인 두가지는 함수로서 호출하는 경우와 메서드로서 호출하는 경우이다.
  • 두 개의 차이는 바로 독립성인데, 함수는 독립적으로 기능을 수행하지만, 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행한다.
  • ※메서드는 '객체의 프로퍼티에 할당된 함수'라는 말은 반은 맞고 반은 틀리다. 할당한다 해서 무조건 메서드가 되는것이 아니라 객체의 메서드로서 호출할 때만 메서드로 동작하고, 아니면 함수로 동작한다.

  • 1번째 줄에서 func라는 변수의 익명함수를 할당했고, 4번째 줄에서 func를 호출했더니 this로 전역객체(window)가 출력된다
  • 6번째 줄에서 변수에 객체를 할당했고, 그 객체 메서드 프로퍼티에 앞에서 만든 func 함수를 할당하고, 9번째 줄에서 obj의 메서드를 호출했더니, 이번엔 this가 obj라고 한다. obj.method와 func 모두 1번째 줄에서 선언한 func 함수를 참조하는데 말이다.
  • 즉, 변수에 담아 호출한 경우와 obj 객체의 프로퍼티에 할당해서 호출한 경우 this가 다르다는 것이다.
  • 그렇다면 '함수로서의 호출''메서드로서의 호출'을 구분하는 방법은 뭐냐? 함수앞에 점(.)의 유무 차이, 이게 끝이다
  • 메서드 내부에서의 this는 호출한 주체에 대한 정보, 즉 함수명 앞의 객체이다.(obj.method)라면 method함수명 앞의 객체인 obj가 this라는 것이다.)

1-3) 함수로서 호출할 때 그 함수 내부에서의 this

  • 함수 내부에서의 this는 정의 되지않는다. this에는 호출한 주체에 대한 정보가 담기는데, 함수로서 호출하는 것은 호출 주체를 명시하지 않고 개발자가 코드에 직접 관여해서 실행한 것이기 때문에 호출 정보를 알 수 없기 때문이다.
  • this가 지정되지 않으면 전역 객체를 바라보므로 함수의 this는 전역객체가 되는 것이다.
  • 그렇다면 함수에서의 this를 사용하려면? ES5까지는 변수 활용을 통해 우회해야했다.

코드처럼 변수를 이용해(통상적으로 self로 쓴다) 우회하여 this를 사용한다

  • 하지만 ES6부턴 실행 컨텍스트를 생성할때 this 바인딩 과정 자체를 생략하는 화살표 함수를 도입하여 우회할 수 있게되었다.

코드처럼 내부함수를 화살표함수로 사용하면 this의 사용이 가능하다.

1-4) 콜백 함수 호출 시 그 함수 내부에서의 this

  • ※콜백함수란? 함수 A의 제어권을 다른 함수(또는 메서드) B에게 넘겨주는 경우 함수 A를 콜백 함수라 한다.
  • 이때 함수 A는 함수 B의 내부 로직에 따라 실행되며, this 역시 함수 B 내부 로직에서 정한 규칙에 따라 결정된다.
  • 콜백함수도 함수이기에 기본적으론 전역객체를 this로 삼지만, 제어권을 받은 함수에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조하게 된다.

1-5) 생성자 함수 내부에서의 this

  • 생성자 함수의 this는 곧 새로 만들 구체적인 인스턴스 자신이다.

Cat 변수의 this는 밑에 코드에서 생기게 되는 인스턴스 자신들이다.


2. 명시적으로 this를 바인딩하는 방법

앞 절에서 상황별로 this에 어떤 값이 바인딩되는지를 살펴봤고

이번 절에선 이러한 규칙을 깨고 this에 별도의 대상을 바인딩 하는것을 알아본다

2-1) call 메서드

  • call 메서드는 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령이다.
  • 이때 call 메서드의 첫번째 인자를 this로 바인딩하고, 그 뒤 인자들을 호출할 함수의 매개변수로 한다.

call을 사용해 this를 지정하는 코드
메서드에서도 마찬가지로 동작한다

2-2) apply 메서드

  • 기능적으로 call과 완전 동일하다.
  • 두번째 인자로 배열을 받고 그 배열의 요소들을 함수의 매개변수로 사용한다는 차이만 존재

apply를 이용, 두번째 인자를 배열로 받아 매개변수로 삼는다.

2-3) call / apply 메서드의 활용

  • 마땅한 대안이 없던 ES5,6 버전 이전엔 실무에서 널리 통용되던 방법

위와 같이 call, apply를 활용하면 반복을 줄이고 효율을 높일 수 있다.

객체에는 배열메서드를 직접 적용할 수 없는데 위 코드와 같이 call과 apply를 이용해 객체에서 배열메서드를 사용할 수도 있다

2-4) bind 메서드

  • ES5에서 추가된 기능으로, call과 비슷하지만 즉시 호출하지는 않고 넘겨 받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드이다
  • 다시 새로운 함수를 호출할때 인수를 넘기면 앞서 bind 메서드에서 전달했던 인수들의 뒤에 등록된다.
  • 즉, bind 메서드는 함수에 this를 미리 적용하는 것과 부분 적용 함수를 구현하는 것 두가지 목적을 가진다

위와 같이 bind 메서드를 사용해 this를 미리 적용할 수도, 함수의 {4,5} {6,7}을 구분해서 선언할 수도 있다.

※bind 메서드를 적용해 새로만든 함수는 name 프로퍼티에 bind의 수동태인 bound가 앞에 붙는다. 이 때문에 함수 추적에 call이나 apply보다 더 유용하다

6번째 줄 처럼 bound가 붙어서 나온다

2-5) 화살표 함수의 예외사항

  • ES6에 새로 도입된 화살표 함수는 실행 컨텍스트 생성 시 this를 바인딩하는 과정이 제외 되었다.
  • 즉, 이 함수 내부에는 this가 아예 없으며, 접근하고자 하면 스코프체인 상 가장 가까운 this에 접근한다

코드 4번째에서 화살표함수로 선언했고 5번째 줄의 this는 존재하지 않으므로 스코프체인 상 가장 가까운 outer 함수의 this에 접근하게 된다!!

 

2-6) 별도의 인자로 this를 받는 경우(콜백 함수 내에서의 this)

  • 다음장에서 자세히 다룬다고 한다
  • 콜백 함수를 인자로 받는 메서드 중 일부는 추가로 this로 지정할 객체(thisArg)를 인자로 지정할 수 있는 경우가 있다.
  • 이러한 메서드의 thisArgs 값을 지정하면 콜백 함수 내부에서 this 값을 원하는 대로 변경할 수 있다.
  • 이러한 형태는 내부 요소에 대해 같은 동작을 반복 수행해야하는 배열 메서드에 많이 포진 돼 있으며, 같은 이유로 ES6에서 등장한 Set, Map 등의 메서드에서도 일부 존재한다.