자바스크립트 this와 bind

0

Javascript

목록 보기
6/13
post-thumbnail

자바스크립트에서 this란 함수를 호출한 객체를 뜻합니다.

this란 무엇인가

  영어 단어 this는 '이것'을 뜻하는 지시어입니다. 그렇다면 자바스크립트에서 this란 무엇일까요? 자바스크립트에서 '이것'이라 칭할만한 것이 있을까요? 일상생활에서 갑자기 '이것'이 무엇이냐고 묻는다면 질문이 나오게 된 배경과 문맥을 먼저 살펴서 적절한 대답을 해야겠죠? 자바스크립트에서도 마찬가지입니다. this라는것은 그 자체로 정해진 값이 아니라, 어디서 this라는 대상을 참조하는가에 따라 달라집니다. 먼저 다음 코드를 보겠습니다.

function showThis(){
  console.log(this); 
}
showThis();

위의 코드를 보면 showThis함수 안에서 this를 콘솔에 출력합니다. 그리고 전역 코드에서 showThis함수를 호출합니다. 브라우저에서 이 코드를 실행시켜 콘솔에 찍힌 내용을 확인해보면 아래와 같습니다.

콘솔에 Window객체가 찍혔습니다. this는 Window객체를 말하는 것일까요? 정확한 대답은

"showThis함수가 호출된 맥락에서는 this가 Window객체를 지칭하는 것이 맞다"

입니다. 그런데 이 맥락이라고 하는것이 뭘까요? 이번에는 node환경에서 동일한 코드를 실행시켜보겠습니다.


Object [global]이라는 것이 출력됩니다. 이제 위에서 말한 맥락이 어떤것인지 감이 오나요? 아직은 정확한 판단이 들지 않을 겁니다. 브라우저 환경에서 this는 그럼 Window 객체로 고정된 값이고, 노드 환경에서는 global 객체라는 것인가? 하는 생각도 들 수 있습니다. 하지만 this는 그렇게 단순하게 결정되는 것이 아닙니다. 다음 추가 예제를 보겠습니다.

function showThis() {
  console.log(this);
}

const obj = {
  name: "Kenny",
  showThis,
};

obj.showThis();
showThis();

이번에는 obj라는 객체 리터럴을 생성하고 objshowThis라는 메서드에 미리 정의한 showThis함수를 할당했습니다. 그리고 차례로 obj객체의 showThis메서드를 호출하고 그냥 showThis함수를 호출 해봤습니다. 실행결과는 다음과 같습니다.

일단 결과만 보면 objshowThis메서드 호출 결과는 obj가 찍혔고, 전역에서 호출한 showThis의 결과로는 그대로 global 객체가 찍혔습니다.
obj객체의 showThis메서드와 전역 코드에 정의된 showThis함수는 같은 함수입니다. 따라서 같은 console.log(this);라는 코드를 실행합니다. 하지만 결과는 위에 보다시피 다르게 나옵니다. 앞서 말한 함수 호출의 맥락이라는 것이 바로 이 차이입니다. 같은 코드라도, 해당 코드가 포함된 함수를 호출한 "객체"this가 됩니다. 다른말로 말하면, this라는 것은 정적으로 하나의 값을 가리키게 결정되는 것이 아닌, 함수가 호출될 때마다 다른 값(함수를 호출한 객체)을 가지게 되는겁니다. 그래서 this는 동적으로 결정된다고 합니다.

this binding

  이제 this라는 것은 함수를 호출한 객체를 뜻한다고 배웠으니, 위에서 살펴본 예제를 해석해 봅시다. 전역 코드에서 호출한 함수는 전역 객체가 호출한 것으로 간주되어 this는 전역 객체가 됩니다. 브라우저 환경에서도 마찬가지로 Window객체가 가장 상위 객체이기 때문에 전역에서 호출된 함수안의 thisWindow객체가 됩니다. 그럼 이 this라는 키워드가 "결정된다"고 하는 것은 어떤 과정을 거칠까요?

binding

  this가 결정되는 것을 전문용어로 this binding이라고 합니다. 우리가 앞선 예제에서 obj의 메서드로 showThis함수를 호출했을 때 해당 메서드 내부의 this는 함수를 호출한 객체인 obj로 바인딩되었다고 표현합니다. 바인딩의 종류는 2가지가 있는데, 하나는 예제에서처럼 저절로 this값이 바인딩되는 암시적 바인딩, 다른 하나는 직접 특정 값을 this로 바인딩해주는 명시적 바인딩이 있습니다.

명시적 binding

아래 코드처럼 특정 함수의 this값을 bind()메서드를 통해 직접 바인딩할 수 있습니다. 이럴 경우 this는 우리가 바인드해준 값으로 고정되고, 함수를 호출한 객체는 전혀 상관이 없어집니다. 또 한 번 bind해준 함수는 다시 bind해줄 수 없습니다.

function showThis() {
  console.log(this);
}

const obj = {
  name: "Kenny",
  showThis,
};

const obj2 = {
  name: "Ted",
  age: 27,
};

const bindedShowThis = showThis.bind(obj2);
obj.showThis();
bindedShowThis();
showThis();

위 코드를 보면 bindedShowThis라는 변수에 showThis함수를 넣는데, bind메서드를 이용하여 obj2를 바인드해주는 것을 볼 수 있습니다. 따라서 bindedShowThis함수를 호출하면, 어디에서 호출하던, 어떤 객체가 호출하던지 상관없이 this는 obj2가 됩니다.

this 활용

그렇다면 this는 왜 존재합니까?

this 키워드를 활용하는 방법은 다양합니다. 어떤 함수에서 쓰이냐에 따라 쓰임새도 많습니다.

생성자 함수

생성자 함수에서는 this가 기본적으로 생성하려는 객체를 의미합니다. 따라서 아래와 같이 this를 쓸 수 있습니다.

function C() {
  this.a = 37;
}

var o = new C();
console.log(o.a); // 37


function C2() {
  this.a = 37;
  return {a: 38};
}

o = new C2();
console.log(o.a); // 38

DOM 이벤트 처리기

이벤트 핸들러 함수에서도 this를 활용할 수 있습니다. 이벤트가 fire되면 해당 DOM 요소(객체)가 핸들러함수를 호출하게 됩니다. 그래서 이때의 this는 이벤트가 발생한 DOM 요소를 지칭합니다.

// 처리기로 호출하면 관련 객체를 파랗게 만듦
function bluify(e) {
  // 언제나 true
  console.log(this === e.currentTarget);
  // currentTarget과 target이 같은 객체면 true
  console.log(this === e.target);
  this.style.backgroundColor = '#A5D9F3';
}

// 문서 내 모든 요소의 목록
var elements = document.getElementsByTagName('*');

// 어떤 요소를 클릭하면 파랗게 변하도록
// bluify를 클릭 처리기로 등록
for (var i = 0; i < elements.length; i++) {
  elements[i].addEventListener('click', bluify, false);
}

0개의 댓글