자바스크립트에서 this는 어디서든 사용할 수 있다. 그러나 상황에 따라 this가 바라보는 대상이 달라지게 된다.
그래서 상황별로 this가 어떻게 달라지고, 왜 그렇게 되는지 알고 있어야 한다.
1. 상황에 따라 달라지는 this
자바스크립트에서 this는 기본적으로 실행 컨텍스트가 생성될 때 함께 결정된다. 즉, this는 함수를 호출할 때 결정된다고 할 수 있고 함수를 어떤 방식으로 호출하느냐에 따라 값이 달라지는 것이다.
1-1. 전역 공간에서의 this
전역 공간에서 this는 전역 객체를 가리킨다. 전역 컨텍스트를 생성하는 주체가 바로 전역 객체이기 때문이다.
참고로 브라우저에서 전역객체는 window이고, Node.js 환경에서는 global이다.
전역변수
그래서 전역변수를 선언하면 자바스크립트 엔진은 이를 전역 객체의 프로퍼티로도 할당한다.
자바스크립트의 모든 변수는 특정 객체의 프로퍼티로서 동작하기 때문에, var 연산자를 이용해 변수를 선언하더라고 특정 객체의 프로퍼티로 인식한다. 그 특정 객체란 바로 실행 컨텍스트의 LexicalEnvironment이다.
전역변수 직접 할당
전역 공간에서는 var로 변수를 선언하는 대신 window의 프로퍼티에 직접 할당하더라도 결과적으로 var로 선언한 것과 똑같이 동작할 것이라고 예상할 수 있다. (대부분의 경우 이 말이 맞다.)
그러나 삭제 명령에서는 전역변수 선언과 전역객체의 프로퍼티 할당이 다르다. 처음부터 전역객체의 프로퍼티로 할당은 b의 경우에는 삭제가 되는 반면, 전역변수로 선언한 a의 경우에는 삭제가 되지 않는다.
바로 전역변수를 선언하면 자바스크립트 엔진이 이를 자동으로 전역객체의 프로퍼티로 할당하면서 추가적으로 해당 프로퍼티의 configurable 속성(변경 및 삭제 가능성)을 false로 정의하기 때문이다.
1-2. 메서드로 호출할 때 그 메서드 내부에서의 this
함수 vs 메서드
어떤 함수를 실행하는 방석에는 일반적으로 두 가지 방법이 있다.
- 함수로서 호출: 그 자체 독립적 기능 수행
- 메서드로서 호출: 자신을 호출한 대상 객체에 관한 동작 수행
함수는 그대로인데 변수에 담아 호출한 경우와 obj 객체의 프로퍼티에 할당해서 호출한 경우에 this가 달라지게 된다. '함수로서 호출'과 '메서드로서 호출'을 구분하는 방법은 함수 앞에 점(.)의 여부로 간단하게 구분할 수 있다. (대괄호 표기법도 메서드로서 호출함.)
메서드 내부에서의 this
this에는 호출한 주체에 대한 정보가 담긴다. 어떤 함수를 메서드로서 호출하는 경우, 호출 주체는 바로 함수명(프로퍼티명) 앞의 객체이다. 쉽게 표현하자면 점 표기법의 경우 마지막 점 앞에 명시된 객체가 곧 this가 된다.
1-3. 함수로서 호출할 때 그 함수 내부에서의 this
함수 내부에서의 this
어떤 함수를 함수로서 호출할 경우에는 this가 지정되지 않는다. 따라서 함수에서의 this는 전역 객체를 가리키게 된다.
메서드의 내부함수에서의 this
내부함수도 함수로서 호출했는지, 메서드로서 호출했는지만 파악하면 this의 값을 맞출 수 있다.
innerFunc를 호출할 때 함수명 앞에 점(.)이 없어 즉 함수로서 호출한 것이므로 this가 지정되지 않았고, 자동으로 전역객체인 window가 바인딩된다.
즉, 오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지 없는지가 관건이다.
메서드의 내부 함수에서의 this를 우회하는 방법
내부 함수에 this를 상속하는 방법이 없지만 이를 우회해서 사용할 수 있다. 그 중 대표적인 방법은 바로 변수를 활용하는 것이다.
outer 스코프에서 self라는 변수에 this를 저장한 상태에서 호출한 innerFunc2의 경우 self에는 객체 obj가 출력된다.
this를 바인딩하지 않는 함수
ES6에서는 함수 내부에서 this가 전역객체를 바라보는 문제를 보완하고자, this를 바인딩하지 않는 화살표 함수를 새로 도입하였다. 화살표 함수는 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠지게 되어, 상위 스코프의 this를 그대로 활용할 수 있게 된다.
1-4. 콜백 함수 호출 시 그 함수 내부에서의 this
콜백 함수란? 함수 A의 제어권을 다른 함수(또는 메서드) B에게 넘겨주는 경우 함수 A를 콜백 함수라고 한다. 이때 함수 A는 함수 B의 내부 로직에 따라 실행되며, this 역시 함수 B 내부 로직에서 정한 규칙에 따라 값이 결정된다.
setTimeout 함수와 forEach 메서드는 그 내부에서 콜백 함수를 호출할 때 대상이 될 this를 지정하지 않는다. 따라서 콜백 함수 내부에서의 this는 전역객체를 참조한다.
그러나 addEventListener 메서드는 콜백 함수를 호출할 때 자신의 this를 상속하도록 정의돼 있다. 그래서 메서드명의 점(.) 앞부분이 this가 된다.
1-5. 생성자 함수 내부에서의 this
생성자 함수는 어떤 공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수이다.
객체지향 언어에서 생성자를 클래스, 클래스를 통해 만든 객체를 인스턴스라고 한다. 예를 들어 공통 속성들을 모아 인간 집합을 정의한 것이 바로 클래스이며, 각 사람들은 인간 클래스에 속하는 인스턴스이다.
자바스크립트에서 new 명령어와 함께 함수를 호출하면 해당 함수가 생성자로서 동작하게 된다. 그리고 어떤 함수가 생성자 함수로서 호출된 경우 내부에서의 this는 곧 새로 만들 구체적인 인스턴스 자산이 된다.
Cat이란 변수에 익명 함수를 할당하고, 함수 내부에서는 this에 접근해서 bark, name, age 프로퍼티에 각각 값을 대입한다. 6, 7번째 줄에서 실행한 생성자 함수 내부에서의 this는 choco 인스턴스와 nabi 인스턴스를 가리킴을 알 수 있다.
2. 명시적으로 this를 바인딩하는 방법
기존 규칙을 깨고 this를 별도의 대상을 바인딩하는 방법도 있다.
2-1. call 메서드
caa 메서드는 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령이다. 이때 call 메서드의 첫 번째 인자를 this로 바인딩하고, 이후 인자들을 호출할 함수의 매개변수로 한다.
함수를 그냥 실행하면 this는 전역객체를 참조하지만 call 메서드를 이용하면 임의의 객체를 this로 지정할 수 있다.
2-2. apply 메서드
apply 메서드는 call 메서드와 기능적으로 완전히 동일하다. call 메서드는 첫 번째 인자를 제외한 나머지 모든 인자들을 호출할 함수의 매개변수로 지정하는 반면, apply 메서드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다는 점에서만 차이가 있다.
2-3. call / apply 메서드의 활용
유사배열객체에 배열 메서드를 적용
객체에는 배열 메서드를 직접 적용할 수 없다. 그러나 배열의 구조와 유사한 객체의 경우(유사배열객체) call 또는 apply 메서드를 이용해 배열 메서드를 차용할 수 있다.
call / apply를 이용해 형변환하는 것은 'this를 원하는 값으로 지정해서 호출한다'라는 본래의 메서드의 의도와는 다소 동떨어진 활용법이라고 할 수 있다.
그래서 이에 ES6에서는 유사배열객체 또는 순회 가능한 모든 종류의 데이터 타입을 배열로 전환하는 Array.from 메서드를 새로 도입했다.
생성자 내부에서 다른 생성자를 호출
생성자 내부에 다른 생성자와 공통된 내용이 있을 경우 call 또는 apply를 이용해 다른 생성자를 호출하면 간단하게 반복을 줄일 수 있다.
여러 인수를 묶어 하나의 배열로 전달하고 싶을 때 - apply 사용
여러 개의 인수를 받는 메서드에서 하나의 배열로 인수들을 전달하고 싶을 때 apply 메서드를 사용하면 좋다.
참고로 ES6에서는 스프레드 연산자(펼치기 연산자)를 이용하면 apply를 적용하는 것보다 더욱 간편하게 작성할 수 있다.
2-4. bind 메서드
bind 메서드는 call과 비슷하지만 즉시 호출하지는 않고 넘겨 받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드이다. 즉, 함수에 this를 미리 적용하는 것과 부분 적용 함수를 구현하는 두 가지 목적을 모두 지닌다.
name 프로퍼티
bind 메서드를 적용해서 새로 만든 함수는 name 프로퍼티에 동사 bind의 수동태인 'bound'라는 접두어가 붙는다. 어떤 함수의 name 프로퍼티가 'bound xxx'라면 이는 곧 함수명이 xxx인 원본 함수에 bind 메서드를 적용한 새로운 함수라는 의미가 되므로 기존의 call이나 apply보다 코드를 추적하기에 더 수월해진 면이 있다.
상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달하기
메서드의 내부함수에서 메서드의 this를 그대로 바라보기 위해 self 등의 변수를 활용하는 우회법 대신에 call, apply 또는 bind 메서드를 이용하면 더 깔끔하게 this를 바라보게 할 수 있다.
2-5. 화살표 함수의 예외사항
ES6에 새롭게 도입된 화살표 함수는 실행 컨텍스트 생성 시 this를 바인딩하는 과정이 제외되었다. 즉, 함수 내부에는 this가 아예 없으며, 접근하고자 하면 스코프체인상 가장 가까운 this에 접근하게 된다.
3. 정리
명시적 this 바인딩이 없는 한 아래 규칙은 늘 성립한다.
- 전역공간에서의 this는 전역객체를 참조한다.
- 어떤 함수를 메서드로서 호출하는 경우 this는 메서드 호출 주체(메서드명 앞의 객체)를 참조한다.
- 어떤 함수를 함수로서 호출한 경우 this는 전역객체를 참조한다. 메서드의 내부함수에서도 같다.
- 콜백 함수 내부에서의 this는 해당 콜백 함수의 제어권을 넘겨받은 함수가 정의한 바에 따르며, 정의하지 않는 경우에는 전역객체를 참조한다.
- 생성자 함수에서의 this는 생성될 인스턴스를 참조한다.
명시적 this 바인딩은 다음과 같다.
- call, apply 메서드는 this를 명시적으로 지정하면서 함수 또는 메서드를 호출한다.
- bind 메서드는 this 및 함수에 넘길 인수를 일부 지정해서 새로운 함수를 만든다.
- 요소를 순회하면서 콜백 함수를 반복 호출하는 내용의 일부 메서드는 별도의 인자로 this를 받기도 한다.
'💜 프론트엔드 > JavaScript' 카테고리의 다른 글
[코어자바스크립트] 04. 콜백 함수 (0) | 2025.01.15 |
---|---|
[코어자바스크립트] 02. 실행 컨텍스트 (1) | 2025.01.11 |
[코어자바스크립트] 01. 데이터 타입 (1) | 2024.12.23 |
[JavaScript] 이벤트 버블링, 캡쳐링, 위임 & HTTP 메소드 (0) | 2024.04.07 |
[JavaScript] var, let, const 중복 선언 허용, 스코프, 호이스팅 (0) | 2024.03.31 |
FE 개발자가 되고 싶은 짱잼이
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!