this는 javascript 예약어로 수행하는 주체가 되는 인스턴스를 지칭하는 자기 참조 변수이다. 상황에 따라 this가 지칭하는 대상이 다르기 때문에 상황별로 어떤 것을 참조하는 지 알아둘 필요가 있다.
난 일을 할때 this에 대한 정확한 개념을 갖고 쓰진않았다. 그냥 여기서 this쓰면 이놈이겠지? 하면서 썼다. 근데 그게 맞았다. 내 자랑이 아니라 그만큼 this는 사용함에 있어 어느정도 추측이 가능하단 얘기다. 그래도 이 글을 읽는 사람들은 this의 사용 위치에 따라 정확히 어떤 것을 참조하는 지 알고 싶어서 읽는 것이라고 생각한다. 그래서 조사해봤다.
this는 javascript의 예약어다. 여기서 예약어는 기본적으로 제공하는 녀석이란 말이다. 그 의미를 잘 생각해보면 알수 있듯 this는 생성하는 객체가 아니란 얘기다. this는 바인딩 과정을 통해 참조하는 대상이 변경된다. 언뜻 당연한 얘기같지만 생성과 바인딩은 천지차이다. 이 점을 명심하자. (이건 좀 오반가?)
바인딩이란 변수와 값을 연결하는 과정을 말한다. this 바인딩은 this (키워드지만 식별자의 역할을 한다.) 와 this가 가리킬 객체를 바인딩하는 것이다.
아까 말했듯이 this
는 어디서 사용하는 지에 따라 바인딩되는 패턴이 다르다. 총 6가지 정도로 분류할 수 있다.
말 그대로 전역에서 this
를 사용한 경우다. 기본적으로 global object (window
객체) 를 지닌다.
console.log(this);
// Window {0: global, 1: global, 2: global, window: Window, …}
일반 함수 내부에서의 this
도 window
객체다. 전역에서 실행된 함수기 때문에 별달리 바인딩이 일어나지 않는다. 그니깐 함수를 호출하는 놈이 누군가. 바로 window
다. 그놈이 바인딩된다고 생각하면 쉽다.
function fn1 () {
console.log(this);
}
fn1();
// Window {0: global, 1: global, 2: global, window: Window, …}
호출 대상으로 this
를 판단한다면 일반 함수 내부에서 함수를 선언하여 호출한다고 그 호출한 함수가 this
로 바인딩되는 것이 아니다. 똑같이 window
객체를 참조한다. 실행 컨텍스트의 프로세스를 생각해보면 쉽다.
function fn1 () {
console.log('fn1 : ' + this);
function fn2 () {
console.log('fn2 : ' + this);
function fn3 () {
console.log('fn3 : ' + this);
}
fn3();
}
fn2();
}
fn1();
//fn1 : [object Window]
//fn2 : [object Window]
//fn3 : [object Window]
단 예외가 있다. ES5 엄격모드 (use strict
) 를 설정하면 this
는 undefined
처리가 된다. 디폴트 바인딩이 없어서 그런건데 설명하면 기니깐 그냥 this
가 편법이라 안되나보다 하핫! 이러고 넘어가자. 또 함수 내부에서 선언된 화살표 함수는 this
가 바인딩되는 방식이 다른데 이건 뒤에서 다루겠다. 아우 많다 많어
'use strict';
function fn1 () {
console.log(this);
}
fn1();
// undefined
일반 함수 호출형태가 아닌 메소드 호출이라면 얘기가 다르다. 호출하는 주체가 일반 함수의 경우 window
지만 메소드의 경우 객체 내부에 포함된 함수기 떄문에 해당 객체가 this
에 바인딩 된다. 뭔소린지는 다음 코드를 보면 이해가 될 것이다.
var name = 'durrian';
var age = 28;
function getInfo () {
return `hi, i'm ${this.name}. age is ${this.age}`;
}
getInfo();
//"hi, i'm durrian. age is 28"
var carrots = {
name : 'carrotsman',
age : 18,
getInfo : function () {
return `hi, i'm ${this.name}. age is ${this.age}`;
}
}
carrots.getInfo();
// "hi, i'm carrotsman. age is 18"
호출하는 주체가 getInfo
함수는 window, carrots.getInfo()
는 carrots
객체기 때문에 this
가 다르게 바인딩된 것을 확인할 수 있다.
이벤트 핸들러에서 this
는 이벤트를 받는 HTML 요소를 바인딩한다. 버튼이면 버튼, 인풋박스면 인풋박스 뭐 이렇단 얘기다.
var btnLogin = document.querySelector('#btnLogin')
btnLogin.addEventListener('click', function () {
console.log(this);
});
// #btnLogin element
새로 생성하는 인스턴스가 this
에 바인딩된다. new
키워드를 빼먹는 순간 fn1
이 지칭하는 this
는 window
가 되니 주의하자.
function fn1(name) {
this.name = name;
}
var a = new fn1('carrots');
var b = new fn1('durian');
console.log(a.name);
console.log(b.name);
// carrots
// durian
var name = 'ggingggang';
var c = fn1('tomato');
console.log(window.name);
// tomato
요약해서 this
에 원하는 객체를 call
, apply
, bind
를 통해 바인딩할 수 있다. call
, apply
, bind
각 키워드의 차이는 호출되는 함수에 파라미터를 전달하는 방식에 차이가 있다. 예제 코드를 보면 알 수 있다.
var name = 'durian';
var obj = {
name : 'carrots',
basicFn : function () {
resultFn('진심');
},
useCall : function () {
// this에 obj 바인딩
resultFn.call(this, '진심');
},
useApply : function () {
// this에 obj 바인딩
resultFn.apply(this, ['진심']);
},
useBind : function () {
// this에 obj 바인딩
(resultFn.bind(this))('진심');
}
}
function resultFn (i_real) {
console.log(`난 ${i_real} ${this.name}을 사랑합니다.`);
}
obj.basicFn(); // 난 진심 durian을 사랑합니다.
obj.useCall(); // 난 진심 carrots을 사랑합니다.
obj.useApply(); // 난 진심 carrots을 사랑합니다.
obj.useBind(); // 난 진심 carrots을 사랑합니다.
기존의 javascript에서 바인딩 해주는 this
를 사용하면 전역으로 선언되어있는 name
, 즉 망할 'durian'
을 참조해 두리안을 진심으로 사랑하는 정신 이상자가 되어버린다. 하지만 call
, apply
, bind
를 통해 this
에 obj
를 바인딩시켜 원하는 결과를 얻을 수 있다.
call
은 함수 파라미터를 ,
로 구분하여 전달하고apply
는 함수 파라미터를 배열로 전달한다.bind
는 call
과 apply
와 달리 호출하는 구문이 아니라 bound함수를 선언하는 구문이다. 그니깐 함수를 리턴해준다 이 말이다.이건 유형이라고 하긴 뭐하고 바인딩되는 대상이 객체가 아니라 context인 녀석이다. 화살표 함수에서 this
는 자신을 감싼 정적 범위 Lexical context 를 가리킨다. 일반 함수처럼 전역에서 호출되는 경우 전역 객체를 가리키는 건 동일하지만 call
, apply
, bind
를 통해 this
의 바인딩을 명시하더라도 무시된다는 얘기다. 쉽게 정리하면 화살표 함수로 선언된 녀석은 어떤 똥꼬쇼를 해도 얘한테는 그냥 호출한 놈이 짱이다 이거다.
오늘은 javascript의 this와 사용하는 방법에 따라 바인딩되는 대상이 다르다는 것을 알아봤다. 나도 감으로만 익혔지 직접 자료 조사하면서 느낀거지만 피곤한 스타일이다. 예외가 어찌나 이렇게 많은지.. 혐오스럽지만 프론트엔드 개발자라면 어떤 종류가 있고 이 놈이 바라보는게 뭔지는 알아야 되지 않을까
오늘 저녁은 추어탕이다. 🥕
참조 : https://seoramyeon.tistory.com/23
https://nykim.work/71
https://paperblock.tistory.com/44