제이크's 자바스크립트 팁 #1, This 제대로 알고가자

Jake Seo·2020년 1월 26일
6

jakes-javascript-tips

목록 보기
1/2

Jake's javascript tips

이전까지는 자바스크립트 개발자라면 알아야 할 33가지 컨셉에 대한 번역글을 주로 올렸었다. 지금 다시보면 굉장히 허접한 번역글인데, 번역 이후에 무언가 나만의 컨텐츠를 진행하고 싶다는 욕심이 생겨서 자바스크립트에서 내가 헷갈렸던 부분만 따로 다시 공부하여 정리하도록 할 것이다.

그 첫째 시간은 This이다.

자바스크립트에서 'this'란 무엇일까?

this는 일반적으로 객체지향 프로그래밍에서 객체 자기 자신을 가리키는 키워드로 사용됩니다.

this는 객체를 가리킨다? 객체란?

그렇다면 객체란 무엇일까요? 컴퓨터 과학에서는 객체란 식별자(Identifier)로 참조할 수 있는 메모리 상의 값입니다. 식별자란 변수의 이름과 같은 것을 의미합니다. 하지만 자바스크립트와 같은 언어적 관점에서의 객체는 조금 다릅니다. 자바스크립트에서의 객체는 일반적으로 속성들(attributes)을 담고있는 가방정도로 생각하면 됩니다.

여기서 속성들(Attributes)이란, 객체에 딸려있는 값을 말합니다. 이를테면 나(me)라는 객체를 생성한다면 다음과 같은 코드로 표현될 수 있을 것입니다.

let me = {'name': 'jake seo', 'age': 27};

위의 예제 코드에서 me라는 객체가 있습니다. 그리고 이름을 의미하는 name이라는 속성을 갖고 있고, 그 속성 값으로 'jake seo'라는 문자열이 들어있습니다. 또, me라는 객체에는 나이를 의미하는 age라는 속성도 갖고 있고, 내부에는 27이라는 숫자 값이 들어있습니다.

여기서 'jake seo'27과 같은 속성이 없는 일반 변수 값을 우리는 Primitive(프리미티브, 원시) 값이라고 합니다. Primitive 타입이란, 객체와 같이 속성(attributes)을 갖고 있지 않은 원시적인 값을 의미합니다. 이를테면 문자열, 숫자 등은 그 자체로 값이 되고 객체와 같은 속성을 가지지 않습니다.

자바스크립트에서는 거의 모든 것이 객체로 표현됩니다. 제가 아는 한, Primitive 타입을 제외한 모든 것이 객체로 표현됩니다. Primitive 타입의 종류로 어떤 것이 있는지 궁금하시다면 여기 Mozilla Javascript MDN의 상세한 설명 글에서 보실 수 있습니다.

그렇다면 자바스크립트에서 this는 왜 중요한가?

개인적으로, 자바스크립트에서 this는 잘 모를 경우에 개발자들에게 삽질을 유발하기 때문에 중요하다고 생각합니다. this를 모른다면, 단순히 this를 모르는 것으로 끝나지 않고 자바스크립트를 공부하다보면 등장하는 심화된 개념들을 이해하는데도 방해가 된다고 생각합니다. 그리고 this를 알아둠으로써 자바스크립트의 내부 동작에 대해서도 아주 조금이나마 더 잘 이해할 수 있다고 생각합니다.

this와 window 객체

1. 자바스크립트의 함수 내부에서 this는 기본적으로 window 객체를 가리킨다?

일단, window 객체란 무엇일까요? 윈도우 객체란 브라우저상에서 사용하는 자바스크립트의 전역객체를 의미합니다. window객체에 대한 상세한 내용은 여기 제로초님 블로그에서 보실 수 있습니다.

브라우저의 콘솔창을 열어 아래의 코드를 입력해보세요. 다음과 같은 결과가 나올 것입니다.

function whatIsThis(){
    return this;
}

console.log(whatIsThis());

2. 그렇다면 this가 window 객체를 가리키지 않으려면?

this가 기본적으로 window 객체를 가리킨다?는 가설을 세웠으면, window 객체를 가리키지 않는 경우를 알아봅시다.

CASE 1. 바인딩을 이용한 방법
모든 함수에 내장된 call, applybind 메소드를 사용하는 경우, 우리는 바인딩을 이용했다고 합니다.

예제 코드 1.
함수의 this로 어떤것이 오는지 테스트하기 위하여 아래와 같은 함수를 만들어봅시다. 아래 함수는 arg라는 인자를 받아서 arg로 받은 인자와 this를 console.log메소드를 통하여 보여줄 겁니다.

function thisTest(arg){
    console.log(arg, this);
}

콘솔창에서 위 함수를 선언하고, thisTest('test')를 수행한 결과는 다음과 같습니다.

콘솔창에서 우리가 보냈던 인자인 'test'문자열이 출력되고 그 이후 해당 함수의 기본 this인 window 객체가 출력되는 것을 볼 수 있습니다.

그렇다면 해당 함수가 갖는 thiscall, apply 또는 bind 함수를 이용하여 바꾸어봅시다.

먼저 this로 삼을 객체를 하나 만들어보겠습니다. 맨 처음의 정의를 기억하신다면 this는 프로그래밍에서 객체 자신을 가리키기 위한 키워드로 사용된다고 하였습니다.

'객체 자신'으로 이용될 객체를 하나 만들어봅시다. this로 사용될 객체를 다음과 같이 구성해보겠습니다.

let customThis = {
    message: 'it is custom this!!'
}

위의 객체가 올바르게 this에 바인딩 된다면, 이전에 콘솔창에서 Window가 출력됐던 부분에 우리가 정의한 customThis의 내용이 나오게 될 것입니다.

콘솔 창에 .call 메소드를 이용하여, 다음과 같이 타이핑하면, 인텔리센스(자동완성) 메세지를 통하여, 앞의 인자는 This로 사용될 인자(Arg)이고, 뒤의 인자는 원래 함수의 인자를 넣어야 하는 것을 알 수 있습니다.

함수의 this 객체는 customThis 객체를 넣고, 함수의 인자로는 'test'문자열을 넣어봅시다.

결과는 다음과 같습니다.

성공입니다. 이전에 Window 객체가 표시되던 자리에 우리가 인위적으로 넣어준, customThis 객체의 내용이 나오고 있습니다.

다음으로는 apply 메소드를 이용해봅시다. apply 메소드는 call 메소드와 거의 똑같지만, 인자를 배열로 받는다는 점만 다릅니다.

이를테면 인자를 두개 받을 때, call 메소드를 이용하여 호출하면 (인자1, 인자2) 이러한 식으로 인자를 넣지만, apply 메소드를 이용하여 호출하면 [인자1, 인자2]와 같은 형식으로 인자를 넣게되는 겁니다.

우리가 넣을 인자는 어차피 한개이니 다음과 같이 작성해봅시다. thisTest.apply(customThis, ['test']);

결과는 다음과 같습니다.

마지막으로, bind 메소드를 이용하여, 바인딩을 해봅시다.

개발자도구에서 위의 call과 같은 형식으로 bind 메소드를 작성하고 엔터를 쳐봅시다.

그러면 위와 같은 결과가 나오는데, 이는 반환 값으로 함수가 나왔음을 의미합니다. 위의 callapply메소드는 this를 바인딩해서 함수를 즉시 실행했지만, bind메소드는 바인딩이 된 함수를 반환해줍니다.

그렇다면 이 함수를 변수에 할당해서 실행해봅시다.

수행 결과는 위와 같습니다.

thisTestFunc라는 변수에 해당 함수를 할당하고, 수행해보니 call로 this를 바인딩하여 수행했던 것과 같은 결과가 나옵니다.

만일, 특정 객체를 this로 바인딩하고 계속 써야하는 경우 call과 apply로 일일이 실행하는 것보다는 bind 메소드를 이용하여, bind된 함수를 변수에 할당하여 사용하는 것이 편할 것입니다.

CASE 2. new 키워드를 이용하는 방법

new 키워드는 제가 아는 한, 자바스크립트에서 객체지향 언어의 클래스를 흉내내기 위해 나왔습니다. 그래서 new 라는 키워드와 함께 this를 사용하면, 객체지향의 this와 매우 유사하게 동작합니다.

이번에는 new 키워드를 이용하는 예제를 활용하기 위해, 새로이 함수를 작성해봅시다. 이름은 함수의 thisTest2입니다.

function thisTest2(something){
	this.something = something; 
}

thisTest2('가가가가') 를 콘솔창에서 실행해보면 다음과 같이 window 객체의 something 속성이 변경되는 것을 볼 수 있습니다.

이제 이 this가 가리키는 것이 window 객체가 아니도록 new 키워드로 바꿔봅시다.

위의 실행 결과를 보면, newObj라는 새로 만들어지는 객체가 this로 바인딩된 것을 볼 수 있습니다.

this는 사실 실행 컨텍스트를 가리킨다?

위의 내용에서 우리는 this가 기본적으로 window를 가리킨다는 사실을 알아봤고, window 객체를 가리키지 않게 만들기 위해서, call, apply, bind 메소드를 이용해보고, new 키워드도 이용해보았습니다.

하지만 갑자기 this가 실행 컨텍스트를 가리킨다니 이게 무슨 말일까요? 또, 실행 컨텍스트란, 무엇일까요?

실행 컨텍스트란 간단하게 표현하면, 내가 함수를 호출한 시점에 나는 어떤 객체에 위치해있었는가? 입니다. 현실세계로 비유하면, 우리는 세계 어디에 있던지, 전화를 이용하여 지구 어딘가에 있는 누군가를 전화(호출)할 수 있습니다.

실행 컨텍스트란 내가 전화번호부에 있는 누군가에게 전화를 걸 때, 내가 어디에 위치하는지를 의미한다고 생각하면 됩니다.

이를테면 우리가 한국에서 미국에 있는 친구에게 전화를 건다면, 우리의 실행 컨텍스트는 한국입니다. 만일, 미국에서 미국에 있는 친구에게 전화를 건다면 우리의 실행 컨텍스트는 미국입니다. 우리의 실행 컨텍스트는 친구가 어디에 위치하는지가 중요한 게 아닙니다. 우리가 현재 어디에 위치한지가 중요합니다.

이제, this가 우리의 실행 컨텍스트를 가리킨다는 것을 알아봤습니다. 하지만 이게 실제 코드에서 어떻게 표현될 수 있는지 이해되지 않을 수 있으니, 실제 코드로 테스트 해봅시다.

먼저, 예제가 될 객체를 하나 만들어봅시다.

let myObj = {
    name : 'jake seo',
    callMyName : function () {
        console.log(this.name);
    }
}

위와 같이 myObj를 만들어봅시다. callMyName이라는 함수에서는 this.name 값을 console.log를 이용하여 출력합니다.

myObj.callMyName(); 을 콘솔창에 입력하여 실행해봅시다. 결과는 다음과 같습니다.

myObj.으로 myObj라는 객체 내부에 들어와서, callMyName함수를 실행시켰기 때문에, 우리가 의도했던 namejake seo라는 올바른 결과 문자열이 나옵니다.

그렇다면, 위의 callMyName 함수를 window 객체에서 실행해보려면 어떻게 해야 할까요?

바로 위와 같이 실행하면 됩니다. 위와 같이 실행하면, 우리가 실행하는 실행 컨텍스트는 window객체이기에, window가 this로 바인딩되고, console.log(window.name);이 수행됩니다. window의 name은 아무것도 없기 때문에 공백이 출력됩니다.

더 확실히 하기 위해 window.name 속성에 'window.name'이라는 문자열을 주고 다시 실행해봅시다.

결과는 위와 같습니다.

결론

this는 실행 컨텍스트를 가리킨다. 일반적으로 브라우저의 최상위 실행 컨텍스트는 window 객체이므로 우리가 간단한 임의의 함수를 만들어 this를 출력해보면 window객체가 출력된다.

this가 window객체를 가리키지 않도록 하려면 바인딩을 이용하거나, 실행 컨텍스트를 변경해야 한다.

바인딩을 이용하는 방법

call, apply, bind메소드를 통하여 가능하다. 또, new 키워드를 이용하면 새로 생성되는 객체가 this로 바인딩된다.

실행 컨텍스트를 변경

실행 컨텍스트를 변경하려면 어떤 객체 내부로 들어가서 함수를 호출하면 된다. 이를테면 객체이름. 과 같이 . 을 이용할 수 있다. 객체이름.메소드명() 으로 메소드를 수행하면 this는 해당객체가 된다.

여담

this, 바인딩, 실행 컨텍스트에 대해 조금 이해가 되시나요? 아마 여기까지 보고 한번에 이해하셨다면, 굉장히 머리가 좋은 분임이 틀림없습니다.

저의 포스팅에서 this에 대한 설명은 이쯤에서 마쳐보도록 하겠습니다. this와 관련된 다른 많은 좋은 글들이 있으니 여러가지 글을 읽고 이해도를 높여보시는 것을 추천합니다.

제로초님의 this 설명
제로초님의 실행 컨텍스트 설명
자바스크립트 MDN this

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글