[JAVASCRIPT] this

박창조·2024년 3월 26일
1

javascript

목록 보기
6/11
post-thumbnail

자바스크립트에서 가장 헷갈리고 혼란스러운 개념을 이야기 하라고 한다면 많은 사람들은 this를 뽑을 것입니다. this는 Java와 같은 객체지향 언어에서 자주 쓰이는 개념이기에 자바스크립트를 다루지 않아도 컴퓨터를 공부하는 누구나 한번쯤은 접해봤을 개념입니다.

흔히 아는 Java에서의 this는 인스턴스 자신(self)을 가리키는 참조변수입니다. this가 객체 자신에 대한 참조 값을 가지고 있다는 의미 입니다. 주로 매개변수와 객체 자신이 가지고 있는 멤버변수명이 같을 경우 이를 구분하기 위해서 사용됩니다. 클래스(Class)에서만 사용할 수 있기 때문에 헷갈리게 될 경우가 거의 없습니다.

그러나 자바스크립트에서의 this는 조금 많이 다릅니다. 그렇다면 자바스트립트에서의 this는 무엇이고, 어떤 이유때문에 다르게 동작하는지 한번 알아보도록 하겠습니다.

this 란❓

자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는
"자기 참조 변수(self-reference variable)"

다른 언어에서와 다르게 자바스크립트에서 this는 클래스 안에서 뿐만아니라 어디에서든 사용할 수 있습니다. 상황에 따라 this가 바라보는 대상이 달라지게 됩니다.

왜 바라보는 대상이 달라지게 될까요?? 그 이유는 자바스크립트의 동작방식에 있습니다.

(this를 알아보기 전 실행 컨텍스트를 먼저 이해하고 오길 권장드립니다.)

자바스크립트에서 this실행 컨텍스트가 생성될 때 함께 결정됩니다. 지난 포스트 “실행 컨텍스트”에서 알아보았듯 함수를 호출하였을 때 실행 컨텍스트가 생성이 될때 “ThisBinding” 이라는 정보를 컨텍스트에 저장하는 것을 보았습니다. 이때 this가 가리키는 정보가 결정되게 됩니다.

다시 말해 this는 “함수를 호출할 때” 결정된다고 할 수 있습니다. 즉, “함수를 어떤 방식으로 호출하느냐”에 따라 this가 가리키는(바인딩)되는 객체가 달라지는 것입니다.

이때 바인딩은 무엇을 의미할까??

바인딩이란?

식별자와 값을 연결하는 과정

쉽게 말해 this라는 키워드에 함수를 호출한 곳의 “객체의 주솟값”을 연결한다는 것입니다.

함수가 호출 될 때마다 상황에 맞게this가 동적으로 결정되고, 이렇게 this가 가리키는 객체가 결정이되었을 때, "this가 그 객체에 binding 된다"라고 표현합니다.

일반적으로 this는 일반적으로 호출 당시 함수를 포함하고 있는 객체에 바인딩에 되지만, 상황에 따라(함수를 호출하는 방식에 따라) 바인딩되는 대상이 달라지게 됩니다.

지금부터 this가 결정되는 다양한 상황을 살펴보도록 하겠습니다.


this 바인딩의 종류

1️⃣ 기본 바인딩 : (전역 컨텍스트, 함수 호출)

전역 공간에서 함수가 단독으로 호출되어 바인딩 되는 방식

1. 전역 컨텍스트에서의 this

자바스크립트 함수를 호출하는 가장 기본적인 방법은 바로 단독 실행하는 것입니다. 

함수를 단독 실행하게되면 이때 전역 공간에서 함수를 호출하였기 때문에 “this는 전역객체에 바인딩”됩니다. 개념상 전역 컨텍스트를 생성하는 주체가 바로 전역 객체이기 때문입니다.

이때 자바스트립트를 실행하는 환경에따라 전역객체에 차이가 있습니다.

브라우저 실행 환경에서는 전역 객체인 “window 객체”에 바인딩

node.js 환경에서는 전역 객체인 “global 객체”에 바인딩

브라우저에서의 전역객체

node.js에서의 전역 객체

➕➕➕

그런데 이때 조심해야 될것은 use strict 키워드를 통해서 엄격모드를 사용하게 되면 전역객체가 기본 바인딩 대상에서 아에 제외됩니다. 따라서 이 경우에는 this가 바인딩 될 객체가 존재하지 않기 때문에 undefined 값을 가지게 됩니다.


2️⃣ 암시적 바인딩 → object 안에서 method로 호출

객체의 메소드(Method)로 호출되어 바인딩 되는 방식

함수 vs 메소드

함수를 실행하는 방법은 여러가지가 있습니다. 그 중 일반적인 방법 두가지는 “함수로서 호출”과 “메소드(method)”로서의 호출 하는 경우입니다.

이 둘의 차이는 무엇일까요?? 바로 “독립성”에 있습니다.

함수는 그 자체로 독립적인 기 능을 수행하지만, 메소드는 자신을 호출한 대상 객체에 관한 동작을 수행합니다.

그래서 전역 공간안에서 사용되는 것이냐, 아니면 객체안에서 사용되는 것이냐에 따라 함수, 메소드라고 부르는 것입니다.

*함수로서의 호출메소드로서의 호출을 구분하는 방법은 “단독으로 사용될 수 있느냐”에 있다. 즉, 함수 앞에 점(.)이 있는지 여부로 구분할 수 있습니다.

앞서 기본바인딩에서는 함수가 단독으로 사용될 때 전역객체가 this에 바인딩된다고 하였는데, 그렇다면 메소드로서 호출된다면 this에는 어떤 정보가 바인딩되게 될까요??

this는 함수를 호출한 주체의 대한 정보가 담긴다고 하였습니다. 메소드로서 호출하는 경우에 호출의 주체는 바로 함수 앞의 객체가 됩니다.

다시말해, 메소드를 감싸고 있는 객체, 함수 호출문의 앞의 점(.) 앞에 명시된 객체가 this가 되는 것입니다.

객체의 메소드로도 호출이 됩니다. 이 경우에는 아까 언급했듯이 this가 점 바로 앞에 있는 객체에 바인딩 됩니다.

➕➕➕함수 내부에서 함수를 호출 했을 때의 this

함수 내부에서 "함수 호출 시"

어떤 함수를 함수로서 호출할 경우 this가 지정되지 않습니다. this에는 호출한 주체에 대한 객체 정보가 담긴다고 했지만,

-> this가 지정되지 않는 경우, this전역 객체를 가리킴

"더글라스 크락포트(Douglas Crockford)는 이를 명백한 설계상의 오류라고 지적합니다."

메소드 내부에서 "함수 호출 시"

매소드 내부에서 정의하고 실행한 함수에서의 this는 많은 사람들이 가장 자주 혼란을 느끼는 지점 중 하나 입니다.

앞서 말한 설계상의 오류로 인해 예측되는 것과 실제로 다르게 동작하게 됩니다.

내부함수에서 역시 함수로서 호출했는지, 메소드로서 호출했는지만 파악하면 this의 값을 정확히 맞출수 있습니다.

다음 예제를 통해 알아봅시다.

// 예제


var obj1={
  outer: function(){
    console.log(this) // --------(1)
    
    var innerFunc = function(){
      console.log(this) // --------(2)
    }
    
    innerFunc();
    
    var obj2 = {
      innerMethod: innerFunc 
    };
    
    obj2.innerMethod() // --------(3)
  }
  
};

obj1.outer()

다음 예제의 결과는 과연 어떻게 나올까요?

여러분들께서 결과를 보시고 (2)의 console.log 의 결과에서 머리속에 물음표가 생겼났을 것입니다.

왜 이런 결과를 보여주는 것일까요??

그 이유는 바로 바로 위에서 말했던 설계상의 오류때문입니다.

(2)에서의 함수 호출은 "함수"로써 호출을 한것이기 때문입니다. 같은 함수라도 (3)에서 결과가 다르게 나온 것은 (3)은 메소드로서 호출되었기 때문에 다른 결과가 나온 것입니다.

그렇기 때문에 this를 가리키는 대상을 확실하게 구분하기 위해서는 이 호출문이 "함수"로서 호출 된 것인지, "메소드"로서 호출된 것인지 구분하는 것이 필요합니다.

즉, 함수를 실행하는 당시의 주변 환경(메소드 내부인지, 함수 내부인지 등)은 중요하지 않고,
"오직 해당 함수를 호출하는 구문 앞에 또는 대괄호 표기가 있는지 없는지가 확인 후 구분하면 됩니다."


3️⃣ 명시적 바인팅

상황별로 this에 어떤 값이 바인딩 되는 넘어, 이 규칙을 깨고 this에 별도의 대상을 바인딩하는 방법을 알아보겠습니다.

1. call 메소드

메소드의 호출 주체인 함수를 즉시 실행하도록 하는 명령

call 메소드를 사용하여 함수를 호출하면, 임의의 객체를 this로 지정할 수있습니다.

// call 메소드 예시

var func = function(a, b, c){
  console.log(`this : ${this}, ${a}, ${b}, ${c}`);
};

func(1,2,3)
func.call({x : 1}, 4, 5, 6)

코드를 실행한 결과, 그냥 함수를 호출했을 때와, call 메소드를 이용하여 호출했을 때의 결과가 다르게 나온 것을 볼 수 있습니다.

결과와 같이 call 메소드의 첫번째 인자를 this로 바인딩하고, 이후 인자들을 호출할 함수의 매개변수로 하여 실행합니다.

이렇게 call 메소드를 이용하면 임의의 객체를 this로 바인딩 할 수 있습니다.

2. apply 메소드

메소드의 호출 주체인 함수를 즉시 실행하도록 하는 명령

apply 메소드call 메소드와 기능적으로 완전히 동일합니다.

차이점은 인자로 받는 내용이 다르다는 것 입니다.

call 메소드 는 인자로 첫번 째 인자를 this에 바인딩하고, 나머지 인자를 호출할 함수의 매개변수로 지정하지만,

apply 메소드는 똑같이 첫번 재 인자를 this에 바인딩하지만, 두번째 인자로 호출할 함수의 매개변수를 모아둔 "배열"로 받는 다는 점이 차이가 있습니다.

같은 예제를 apply 메소드로 나타내면 아래와 같습니다.

// apply 메소드 예시

var func = function(a, b, c){
  console.log(`this : ${this}, ${a}, ${b}, ${c}`);
};

func(1,2,3)
func.apply({x : 1}, [4, 5, 6])

결과는 동일하게 나온 것을 확인 할 수 있습니다.

call과 apply를 사용하면 this를 "바인딩할 객체를 지정"한 상태로 함수를 호출할 수 있습니다.

3. bind 메소드

bind 메소드는 동일하게this가 참조하는 객체를 고정시켜 줍니다. 마찬가지로 첫 번째 인자의 this를 바인딩할 객체를 넣어주는 방식으로 사용됩니다.

하지만 다른점은 bind 메소드는 함수를 즉시 호출하는 것이 아니라, 새로운 함수를 반환하기만 하는 메소드 입니다.

// bind 메소드 예제

var func = function(a, b, c){
  console.log(`this : ${this}, ${a}, ${b}, ${c}`);
};

func(1,2,3); 

var bindFunc = func.bind({x : 1});
bindFunc(4,5,6);

예제와 같이 bind 메소드는 함수를 리턴하기 때문에 즉시 실행 되지 않습니다. 그래서 따로 변수에 담아서 지정 후 실행할 수 있습니다.

조금 자세히 살펴보면 func.bind(context)는 함수처럼 호출 가능한 '특수 객체(exotic object)'를 반환합니다.

이 객체를 호출하면 this가 context로 고정된 함수 func가 반환됩니다. 이렇게 항상 같은 객체에 바인딩 되도록 강제 하는 방법'하드 바인딩'이라고도 부릅니다.


4️⃣ new 반인딩 : (생성자 함수로서 호출 시)

자바스크립트 함수를 new 연산자와 함께 호출 하게 되면 생성자 함수로서의 역할을 수행할 수 있게 됩니다.

  1. new 연산자로 호출하면 새로운 객체가 생성됩니다.
  2. 새로 생성된 객체의 prototype이 연결됩니다.
  3. 함수의 코드를 실행합니다.(this 바인딩 👀)
  4. 새로 생성한 객체를 반환합니다.

바인딩 우선순위

(1)new 바인딩 > (2)암시적 바인딩 > (3)명시적 바인딩 > (4)기본 바인딩

화살표 함수에서의 this

화살표 함수를 사용하는 가장 큰 목적중 하나는 상위 실행 문맥을 유지하는 것입니다.

화살표 함수를 통해 함수를 실행하는 것은 다른 함수를 실행하는 방법과 조금 다른 방식으로 동작합니다.

"화살표 함수 this는 선언된 시점의 상위 함수 스코프를 가리킴" -> "함수 정적 바인딩"

그렇기 때문에 자체의 this가 아니라 화살표 함수가 생성된 컨텍스트의this를 가리킵니다. 이를 렉시컬 this라고 합니다.


참조

[JavaScript] This (바인딩 룰, 화살표 함수)

PoiemaWeb

자바스크립트 this 이해하기 (feat. call, apply, bind의 차이)

[JavaScript] this 란 무엇일까? - 하나몬

[JavaScript] 화살표 함수와 this 바인딩

책 <코어 자바스크립트> - 정재남

profile
사랑을 꿈꾸는 냄새나는 개발자 입니다 :)

0개의 댓글