This
는 정말로 제대로 공부하지 않으면 매우 헷갈리는 불편한 존재다..
핵심을 보자면,JavaScript
의 This
는 함수를 호출할때 어떻게 호출했는지에 따라 바뀐다.
당장 개발자 도구(F12)를 열어 This
를 입력해보자
window
이다.
TIP!
window 객체는 브라우저의 요소들과 자바스크립트 엔진, 모든 변수를 담는 객체이다.
함수로 만들어 함수 내부에서의 this
는 다음과 같다.
이번엔 반대로 객체 내부 a
라는 메소드에 this
를 넣어보면 다음과같다.
let obj = {
a: function() {
console.log(this);
},
};
obj.a();
결과는 obj
라는 객체의 메소드 a
는 obj
를 가르킨다.
이번엔 obj.a
를 꺼내서 b
라는 변수에 할당하면, this
는 window
를 출력한다.
let b = obj.a;
console.log(b) // window 어쩌구 저쩌구
그렇기 때문에, JavaScript
에서 this
는 함수로 호출했는지, 객체의 메서드로 호출했는지가 중요하다.
Lexical Scoping 는 함수를 선언할 때 결정된다. 함수의 상위 스코프를 결정하는 방식으로 가장 가까운 위치에서 상위 범위로 변수를 찾는 행위이다.
함수선언시Lexical Scoping
, 함수가 호출시This binding
function a (x, y) {
function b() {
console.log(this);
return x + y
}
return b();
}
a(1,2)
//window
//3
중첩 함수를 쓴다 하더라도 함수 b
내부의 this
는 Window
를 출력한다.
이게 골때리는게, 더 위의 obj
객체 예제에서 a
라는 메소드의 내부함수를 추가로 작성하고 내부함수를 호출하면 내부함수의 this
는 전역객체인 window
를 바인딩한다.
단, 'use strict'
가 작성된 Strict
모드에서는 undefined
.
call
,bind
,apply
삼총사는 this
를 명시적으로 가르키도록 해준다.
그래서 내부함수에서 this
를 사용할 경우 가르키는 대상을 지정할 수 있다.
let obj2 = {c: 'd'}
function b() {
console.log(this);
}
b() //whindow
b.bind(obj2)(); //obj2
b.call(obj2); // obj2
b.apply(obj2); //obj2
let value = 1;
let obj = {
value: 100,
foo: function() {
function bar(a, b) {
console.log(this);
console.log("bar's arguments: ", arguments);
}
bar.apply(obj, [1, 2]);
bar.call(obj, 1, 2);
bar.bind(obj)(1, 2);
}
};
obj.foo();
화살표 함수 () => {}
는 기본적으로this
를 가지지 않는다. 그렇기 때문에 this
를 사용하면 상위 스코프의 값을 가져온다. Lexical This
라고 불리는 이유중 하나이다.
간단한 예를 보면
let obj = {
a: () => { console.log(this)}
}
obj.a() //Window
일반 함수는 obj
를 가르키겠지만 화살표는 보다 상위 스코프인 Window
객체를 가르킨다.
function Prefixer(prefix) {
this.prefix = prefix;
}
Prefixer.prototype.prefixArray = function (arr) {
// this는 상위 스코프인 prefixArray 메소드 내의 this를 가리킨다.
return arr.map(x => `${this.prefix} ${x}`);
};
const pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim']));
이 코드에서는this
는 상위 스코프인 prefixArray
를 가리킨다.
Prefixer {prefix: 'Hi'}