📚 코어자바스크립트 (정재남, 2022) 책과 인프런의 코어자바스크립트 강의를 보고 배우거나 추가적으로 찾아본 것들을 정리한 내용입니다.
객체 지향 언어에서의 this
이번주 면접 스터디 발표 주제는 this! 기술매니저님들 말씀에 따르면 현업에서는 잘 쓰지 않지만 면접에서는 꼭 나온다는 this를 그렇다면 왜 중요하게 생각하고 왜 쓰는 것일까? 부터 찾아보았다.
객체 지향 언어에서 클래스는 하나의 큰 모듈이라고 할 수 있다. 위와 같이 이름, 체력, 마력이 지정되어있고 스킬 사용 시 이 캐릭터의 마력을 50만큼 깎는다고 지정할 때 this를 사용해 이 클래스 내부의 변수를 지칭할 수 있다.
조사해 본 자료에 따르면 자바스크립트는 맨 처음 개발할 때 이러한 객체 지향 개념 없이 만들고 싶었는데, 당시 객체 지향에 익숙해져 있던 개발자들이 객체 지향에서 자주 사용하는 this와 같은 개념들을 많이 만들어서 사용했다고 한다. 클래스를 많이 사용하는 객체 지향 언어에서의 this는 중요하지만, 함수형을 많이 사용하는 자바스크립트 실무에서는 this를 많이 쓰지 않는다고 한다.
따라서 우리는 this가 사용될 때 어떤 데이터를 의미하는지만 간단하게 살펴볼 것이다.
자바스크립트에서의 this
실행 컨텍스트 예제에서 많이 나오는 outer, inner 코드이다. 이때 inner 함수의 실행 컨텍스트를 만들 때 렉시컬까지 만들고 this 바인딩을 진행한다. 따라서 this는 실행 컨텍스트가 활성화될 때, 즉 컨텍스트에 해당하는 함수가 호출되는 그 순간 this가 무엇인지를 가리키게 되기 때문에 값이 상황에 따라 유동적으로 변하게 된다.
상황에 따른 this
상황에 따른 this는 위와 같은 케이스로 분류해서 살펴볼 것이다.
1) 전역 공간에서의 this
전역 공간은 따로 함수가 필요 없기 때문에 브라우저의 콘솔 창에서 바로 this를 출력해 볼 수 있다. 전역 공간에서의 this는 브라우저에서 윈도우 객체를 나타낸다. 전역 컨텍스트를 실행하는 주체가 바로 전역 객체이기 때문이다.
2) 함수 호출 시의 this
왼쪽의 예제는 a 함수를 호출하고 있고 그 내부에서 this를 콘솔로 출력하고 있다. 이때의 this도 윈도우 객체를 가리킨다. 함수를 호출한 순간 this가 무엇을 가리키는지 결정이 되는데 함수를 호출한 주체가 그냥 전역공간이기 때문이다! 따라서 전역 공간에서 this를 호출했을 때와 같이 윈도우 객체를 가리키게 된다.
이번엔 함수 b를 호출하고, 그 안에 새로운 함수 c가 있고, 그 내부에서 this를 콘솔로 출력해보려 한다. 이때도 윈도우 객체를 가리키는데, c는 b 함수 안에 있음에도 불구하고 왜 b가 아니라 전역 객체를 가리키는 걸까?
이에 대해서는 자바스크립트가 일주일 만에 개발된 언어이기 때문에 개발 상의 실수다, 라고 말하는 의견도 있다고 한다. 이러한 불편성을 감안하여 ES6에서 this가 명확하게 어떤 값만 바라보도록 만들어진 것이 화살표 함수이다.
이번엔 d 객체 안에 있는 e라는 key를 호출한다. 객체 안에 key-value 쌍으로 되어있는 값 중 e의 value 값은 함수이다. 그 함수 안에 또 f 라는 함수가 있고 f가 호출되며 this를 콘솔로 찍게 된다.
이때도! 객체 안에, value 안에 있는 this더라도 윈도우 객체를 가리킨다. 왜냐면 메서드로서 e를 호출한 게 아니라 그냥 f 함수 자체를 호출했기 때문이다.
3) 메서드 호출 시의 this
(1) 메서드란?
그렇다면 메서드는 무엇일까?
자바스크립트의 기본 타입은 객체이다. 객체란 왼쪽의 객체 b: function() { ... } 처럼 key-value 쌍으로 구성된 프로퍼티 (프로퍼티란 key-value 쌍을 말한다.) 의 정렬되지 않은 집합을 말한다.
프로퍼티의 값인 key의 value 값으로 함수가 올 수도 있는데, 이러한 프로퍼티를 메서드라고 한다. 메서드 형식은 Math.random()이나 Math.floor()에서도 살펴볼 수 있는데 계산을 쉽게 하기 위해 Math라는 객체를 만들고 그 안의 random이나 floor를 호출하면 어떤 계산을 해서 return해라. 라고 미리 만들어둔 것인데 이러한 형식들을 메서드라고 하는 것이다.
(2) 메서드 호출 시의 this
메서드로 호출됐을 때의 this는 위와 같이 객체.메서드 (=== a.b())와 같이 호출하는데, 호출한 대상 객체 (a) 를 가리킨다. b라는 함수를 상위 a 객체의 메서드로서 호출했기 때문에 호출한 대상이 a 객체이기 때문이다.
a 객체 안에 b라는 key가 있고, b의 value는 어떠한 객체인데 이 객체 안에 또 함수가 있는 다중 구조이다. a안의 b안의 c를 부를 때 앞에 . 이 붙었으니 메서드로서 c를 호출하고 있다. 이때의 this도 호출한 대상 객체를 나타낸다.
다시 함수로서와 메서드로서의 this를 비교해보자. 왼쪽은 메서드로서의 this, 오른쪽은 함수로 호출됐을 때의 this를 부르는 예시 코드이다. 메서드로서의 this는 호출한 대상 객체, 함수로 호출됐을 때는 전역인 window를 가리킨다.
이때, 작은 팁으로 어떻게 함수를 불렀냐에 따라 this가 가리키는 값을 구분해 볼 수 있다. 왼쪽 메서드로서의 this는 a.b가 하얀색으로 앞에 붙고 c 함수를 파랗게 호출한다. 그리고 오른쪽 함수'만'으로서의 this 예시는 옆에 붙는 것도, 하얗게 표시되는 것도 없이 바로 파란색으로 f 함수를 호출한다. 메서드로서의 this는 텍스트 에디터 (우리가 사용하는 VSCode)에서 이렇게 색으로 구분을 해주는 기능이 있다. 따라서 this를 호출하는 함수 앞에 어떤 게 붙어있는지, 점이 있는지, 색이 있는지 등을 보고 메서드로서 불렀는지 함수로서 불렀는지 판단해 볼 수 있다.
메서드로서 호출할 때 객체.함수라고 표현할 수도 있지만 대괄호로도 사용 가능하다. 따라서 위의 obj.func()와 obj['func']()는 같은 의미이고 아래의 person 객체의 info 프로퍼티 안의 getName() 함수를 부를 때도 모두 다르게 생겼지만 동일한 기능을 하는 코드이다.
함수로 호출할 때와 메서드로 호출할 때를 복습해 보자. 맨 처음 obj.b()를 만나 obj 객체 안에 있는 b를 호출한다. b를 내려가다 보니 콘솔로 this의 a를 찍으라고 한다. 이때의 this는 obj 객체의 메서드로 b를 호출했기 때문에 호출한 obj 객체를 가리키고, obj 안에 있는 a값, 즉 20을 뱉어낸다.
그 뒤 아래로 내려가다보니 c를 호출하는데 앞에 . 이나 색이 다르게 붙는 게 없이 그냥 함수로 호출하고 있다. 때문에 c 안의 this는 전역을 가리키고, 전역에 있는 a값인 10을 뱉어낸다.
4) 콜백 호출 시의 this
콜백 함수 개념이 아직 정립되지 않아 상세하게 설명하는 것보다 간단하게 정리했다. 기본적으로는 함수의 this와 동일하나 제어권을 가진 함수가 콜백의 this를 지정하면 그에 따르고, 개발자가 this를 바인딩해서 넘기면 그에 따르게 된다.
5) 생성자 함수 호출 시의 this
person 객체에 이름과 하루에 커피를 몇 잔 마시는지에 대한 name과 coffee가 있다. 이 객체를 부를 때 별도의 생성자 함수 없이 그냥 값을 대입하기만 했다. 이때의 this는 그냥 넘겨준 값, 김한솔과 1만을 출력하게 된다.
같은 코드이지만 이번엔 생성자 함수인 new를 이용해 새로운 person 객체를 만들고 값을 같이 주입한다. 이를 콘솔로 찍어보면 이전에는 그냥 값만 나온 것과 다르게 person 객체 그 자체에 넘겨준 값이 같이 들어가며 인스턴스 값을 통째로 출력한다.
this를 명시적으로 바인딩하는 법
매번 어떻게 호출하냐에 따라서 this를 찾는 것보다 내가 원하는 값을 아예 this로 지정해서 보내주는 방법은 없을까?
1) call, apply, bind
첫 번째 방법은 바로 call, apply, bind를 사용하는 것이다. 괄호 맨 앞에 this로 지정하고 싶은 값을 정하고, 뒤에는 호출한 함수의 매개변수를 부른다. 이 매개변수를 그냥 변수로 보내느냐, 배열로 보내느냐에 따라 사용법이 달라진다.
a 함수를 호출할 때 call을 이용해 this 값을 아예 지정해 보자. 그 뒤에는 매개변수로 1, 2, 3을 넣는다. a 함수는 그것을 받아서 this와 매개변수로 들어온 x, y, z를 콘솔로 찍는데, 코드에서 지정해 준 대로 this의 값은 아래에 있는 변수 b를 가리키고 있기 때문에 콘솔로 찍었을 경우 this가 b 객체를 가리키고 있음을 확인할 수 있다.
2) this값 직접 지정하기
call, apply, bind 외에 기존 개발자들이 사용하던 방식은 바로 this의 값을 코드 상으로 직접 지정해 주는 방식이다.
왼쪽의 코드에서 obj 객체 안의 b 함수를 호출한다. 내려가다 보면 좌측의 코드처럼 this의 a값을 출력하라고 한다. 이때의 this는 obj.b()라고 호출한 것처럼 메서드로서 호출됐기 때문에 객체 obj 안에 있는 a값인 20을 출력한다.
또 내려가다가 함수 c를 호출하고, c 함수 안에는 self의 a를 출력하라고 한다. 여기서 self를 찾아보니 c 안에는 self가 정의되어있지 않다. 따라서 outer인 b의 function으로 범위를 확장시킨다. 이 b 안에 self가 있고, self는 this이다.
그런데 이미 b 함수가 호출될 때 이미 메서드로 호출이 되면서 this를 객체 obj라고 바인딩시킨 상태이다. 그렇기 때문이 이 self의 this는 결국 obj 객체를 나타내고, obj 객체의 a는 20이기 때문에 콘솔은 20을 출력한다.
이렇게 call, apply, bind로 명확하게 지정하거나, 내가 임의로 this값을 만들어서 그 값을 바라보게끔 하는 방법으로 this를 지정할 수 있다.
정리해 보면 전역공간 및 함수 호출 시의 this는 window 객체, 메서드로 호출했을 때는 메서드 호출 주체, 콜백으로 호출했을 때는 기본적으로는 함수와 동일하지만 지정된 값이 있으면 그에 따르도록, 생성자 함수 호출 시의 this는 인스턴스를 가리킨다.
따라서 this를 만나게 되면 이 this를 어떻게 호출했냐에 따라 그 값이 어떻게 나오겠구나, 라고 추측해 보면 좋을 듯하다.
✅ 좋은 책과 좋은 강의 덕분에 this에 대해 쉽게 정리할 수 있었다. 또한 내가 콜백 함수에 대한 개념이 부족했다는 것도 캐치했다! 아직은 this를 사용한 코드를 실제로 본 적이 없지만 나중에 만나게 되면 이번에 공부한 방법으로 이 this가 어떤 값을 가리키는지 파악해 볼 수 있을 것 같다.
[참고 자료]
정재남, 코어 자바스크립트 (위키북스, 2022) p.65 ~ 94
'Front-End > JavaScript' 카테고리의 다른 글
[JavaScript] 모던 자바스크립트의 특징 (0) | 2023.04.28 |
---|---|
[프로그래머스 / JavaScript] Lv.2 피보나치 수 (0) | 2023.04.22 |
[JavaScript] 자바스크립트로 동적 테이블 만들기 (ft. 버튼 클릭) (2) | 2023.04.17 |
[JavaScript] 브라우저 렌더링 과정(원리) (9) | 2023.04.14 |
[JavaScript] 자바스크립트로 숫자야구 프로그램 만들기 (0) | 2023.04.07 |