TIL 029 | 코어 자바스크립트 - this(1)

JU CHEOLJIN·2021년 8월 10일
0

JavaScript

목록 보기
11/13
post-thumbnail

자바스크립트를 공부하면서 정말 어려운 개념을 손꼽자면 this 를 빼놓을 수 없다. 언제는 window 객체를 내놓았다가 언제는 상위 객체를 내놓았다가 하다보니 혼란이 온다. 특히, 콜백 함수로 전달되는 경우에는 예상치 못한 undefined 를 만나고 당황하기도 했다. 이걸 이겨내지 못하면 보통의 개발자도 되지 못하겠지... 어서 this 를 뿌셔보자

상황에 따라 달라지는 this

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

전역 공간에서의 this

전역 공간에서 this 는 전역 객체를 가리킨다. 브라우저 환경에서 전역객체는 window이고 Node.js 환경에서는 global입니다.

메서드로서 호출할 때 그 메서드 내부에서의 this

함수 vs 메서드

프로그래밍 언어에서 함수와 메서드는 미리 정의한 동장을 수행하는 코드 뭉치로, 둘의 차이는 독립성에 있다. 함수는 자체로 독립적인 기능을 수행하는 반면, 메서드는 자신을 호출한 객체에 관한 동작을 수행한다.

기억해야하는 점은 어떤 함수가 객체의 프로퍼티에 할당된다고 해서 무조건 메서드가 되는 것이 아니라 객체의 메서드로서 호출할 경우에만 메서드로 동작하고, 그렇지 않으면 함수로 동작한다.

let obj = {
  method: function(x) {console.log(this, x); } 
};
obj.method(1); // {method: f} 1
obj['method'](2); // {method: f} 2

함수로서의 호출과 메서드로서의 호출을 구분하는 방법은 함수 앞에 점(.)이 있는지 확인하면 된다. 점 표기법이든 대괄호 표기법이든, 어떤 함수를 호출하는 경우에 함수 이름 앞에 객체가 명시돼 있는 경우라면 메서드이다.

메서드 내부에서의 this

this에는 호출한 주체에 대한 정보가 담긴다. 어떤 함수를 메서드로 호출하는 경우 호출 주체는 바로 함수명(프로퍼티명) 앞의 객체이다.

함수로서 호출할 때 그 함수 내부에서의 this

함수 내부에서의 this

this 는 호출한 주체의 정보가 담긴다고 했다. 그런데 함수로서 호출하는 것은 호출 주체가 따로 없고 개발자가 직접 관여해서 실행한 것이기 때문에 호출 주체의 정보를 알 수 없다. 따라서, 전역 객체를 바라보게 된다.

메서드의 내부함수에서의 this

this 바인딩에 대해서 함수를 실행하는 당시의 주변 환경(메서드 내부인지, 함수 내부인지) 등은 중요하지 않다. 오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지 없는지가 관건이다. 예시를 보자.

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

위의 경우를 보면 obj1, window, obj2 가 나온다. innerFunc 의 경우 동일한 함수이지만 함수로서 호출됐는지, 아니면 앞에 객체가 붙어 호출됐는지에 따라 결과가 달라졌다.

메서드의 내부 함수에서의 this를 우회하는 방법

this 가 호출 주체가 없을 때는 자동으로 전역객체를 바인딩하지 않고 주변 환경의 this를 그대로 상속받아 사용할 수 있다면 좋을 것이다. 이를 우회하는 방법으로 변수를 활용할 수 있다.

let obj = {
  outer: function() {
    console.log(this); // (1) {outer: f }
    let innerFunc1 = function() {
      console.log(this); // (2) window {...}
    };
    innerFunc1();
    
    let self = this;
    let innerFunc2 = function(){
      console.log(self); // (3) {outer: f}
    };
    innerFunc2();
  }
};
obj.outer();

outer 스코프에서 self라는 변수에 this 를 할당하고 이를 이용해서 출력하도록 하면 객체 obj가 출력된다.

this를 바인딩하지 않는 함수

기존의 this의 경우 함수 내부에서 전역 객체를 바라보는 문제가 있었다. 이를 보완하기 위해서 ES6에서는 this를 바인딩하지 않는 함수인 arrow function을 도입했다. 화살표 함수의 경우 실행 컨텍스트를 생성할 때 this 바인딩 과정이 빠져서 상위 스코프의 this 를 그대로 활용할 수 있다.

let obj = {
  outer: function() {
    console.log(this); // (1) {outer: f}
   	let innerFunc = () => {
      console.log(this); // (2) {outer: f}
    };
    innerFunc();
  }
};
obj.outer();

innerFunc의 경우에 함수로 호출했음에도 불구하고 this 바인딩 과정이 발생하지 않아 상위 스코프인 outer의 this 를 그대로 사용했다.

콜백 함수 호출 시 그 함수 내부에서의 this

document.body.innerHTML += `<button id='a'>클릭</button>`;
document.body.querrySelector("#a")
  .addEventListener("click", function(e) {
  console.log(this, e);
}));

addEventListener 는 지정한 엘리먼트에 "click" 이벤트가 발생할 때마다 그 이벤트 정보를 콜백 함수의 첫 번째 인자로 삼아 함수를 실행하라는 명령이다. 버튼을 클릭하면 앞서 지정한 엘리먼트와 클릭 이벤트에 관한 정보가 담긴 객체가 출력된다.

여기서 addEventListener는 콜백함수를 호출할 때 자신의 this를 상속하도록 정의돼 있다. 따라서 메서드명의 점(.) 앞부분이 곧 this가 된다.

이처럼 콜백함수에서의 this는 단순하게 무엇이다라고 확답을 내리기 어렵다. 콜백함수의 제어권을 가진 함수가 this를 무엇으로 할지 결정한다. 만약 아무런 정의도 발생하지 않는다면 여전히 기본적인 함수와 마찬가지로 전역객체를 바라본다.

생성자 함수 내부에서의 this

let Cat = function(name, age) {
  this.bark = '야옹';
  this.name = name;
  this.age = age;
};
let choco = new Cat('초코', 7);
let nabi = new Cat('나비', 5);
console.log(choco, nabi);

// Cat {bark: '야옹', name: '초코', age: 7}
// Cat {bark: '야옹', name: '나비', age: 5}

자바스크립트는 함수에 생성자로서 역할을 함께 부여했다. new 키워드를 통해서 함수를 호출하면 해당 함수가 생성자로서 동작하고, 내부의 this는 곧 새로 만들 인스턴스 자신이 된다.

마치며!✨
정리를 하면서도 자꾸만 '어렵다' 라는 생각이 들고 있는 this 에 관해서 정리했다. 다음으로는 사용하는 환경에 따라서 변경되는 this 를 바인딩 하는 방법에 대해서 다뤄보겠다.

profile
사회에 도움이 되는 것은 꿈, 바로 옆의 도움이 되는 것은 평생 목표인 개발자.

0개의 댓글

관련 채용 정보