기본적으로 this는 브라우저 환경에서 window, node 환경에선 global
함수 내부에서의 this
// CASE1 : 함수
// 호출 주체를 명시할 수 없기 때문에 this는 전역 객체를 의미해요.
var func = function (x) {
console.log(this, x);
};
func(1); // Window { ... } 1
// CASE2 : 메서드
// 호출 주체를 명시할 수 있기 때문에 this는 해당 객체(obj)를 의미해요.
// obj는 곧 { method: f }를 의미하죠?
var obj = {
method: func,
};
obj.method(2); // { method: f } 2

함수로서의 호출과 메서드로서의 호출 구분
var obj = {
method: function (x) { console.log(this, x) }
};
obj.method(1); // { method: f } 1
obj['method'](2); // { method: f } 2

호출주체를 나타내는 것을 볼 수 있다.
메서드 내부에서라도 예외는 없이 함수로 호출 되면 전역 객체를 가르킨다.
결론
함수로서의 호출 => 전역 객체,
메서드로서의 호출 => 호출 주체
그러면 이러한 this를 개발자 입장에서 그대로 둘 순 없으니,
우회하는 방법을 찾아보자.
var obj1 = {
outer: function() {
console.log(this); // (1) outer
// AS-IS
var innerFunc1 = function() {
console.log(this); // (2) 전역객체
}
innerFunc1();
// TO-BE
var self = this;
var innerFunc2 = function() {
console.log(self); // (3) outer
};
innerFunc2();
}
};
// 메서드 호출 부분
obj1.outer();

이런식으로 self에다 this를 넣어서 사용 할 수도 있겠지만 별로 좋은 방법은 아닌듯 하다.
두번째 방법으로 ES6의 기술인 화살표 함수를 사용하는 것이다.
var obj = {
outer: function() {
console.log(this); // (1) obj
var innerFunc = () => {
console.log(this); // (2) obj
};
innerFunc();
}
}
obj.outer();

화살표함수를 사용하면 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가
없기 때문에 this는 이전의 값-상위값-이 유지된다(스코프체이닝에 의해).
마지막으로 상황별 규칙을 깨고 명시적으로 this binding을 할 수 있는
call/apply/bind를 사용해보자.
var func = function (a, b, c) {
console.log(this, a, b, c);
};
// no binding
func(1, 2, 3); // Window{ ... } 1 2 3
// 명시적 binding
// func 안에 this에는 {x: 1}이 binding돼요
func.call({ x: 1 }, 4, 5, 6}; // { x: 1 } 4 5 6

call을 통해 this의 값을 지정해줄 수 있다. 만약 호출주체가 있다고 해도
call을 통해 명시적으로 바꿔줄 수 있다.
var func = function (a, b, c) {
console.log(this, a, b, c);
};
func.apply({ x: 1 }, [4, 5, 6]); // { x: 1 } 4 5 6
var obj = {
a: 1,
method: function (x, y) {
console.log(this.a, x, y);
}
};
obj.method.apply({ a: 4 }, [5, 6]); // 4 5 6

apply 역시 뒤에 인자들을 []로 묶어준다면 사용법은 같다.
둘은 즉시실행하는 메서드이다.
이러한 call/apply는 한가지 활용법이 더 있는데
유사배열 객체에서 쓰일 수 있다.
유사배열 객체란?
말그대로 배열과 비슷한 객체라는 뜻이다.
유사배열이 되기위해서는 반드시 length가 필요하다
또한 필수조건은 아니지만 index번호가 0부터 시작해 1씩 증가해야 한다. (안지킬시 어지러워짐)
//객체에는 배열 메서드를 직접 적용할 수 없어요.
//유사배열객체에는 call 또는 apply 메서드를 이용해 배열 메서드를 차용할 수 있어요.
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
Array.prototype.push.call(obj, 'd');
console.log(obj); // { 0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4 }
var arr = Array.prototype.slice.call(obj);
console.log(arr); // [ 'a', 'b', 'c', 'd' ]
하지만 본래의 의도와는 조금 다른 모습이라
ES6부터 도입된 Array.from 메서드를 사용하면 편하다.
근데 유사배열 객체에다 spread operator 쓰면 더 편한 듯?
// 유사배열
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
// 객체 -> 배열
var arr = Array.from(obj);
// 찍어보면 배열이 출력됩니다.
console.log(arr);
bind는 앞선 call/apply와는 다르게 즉시 실행 함수는 아니다.
var func = function (a, b, c, d) {
console.log(this, a, b, c, d);
};
//func(1, 2, 3, 4); window객체
// 함수에 this 미리 적용
var bindFunc1 = func.bind({ x: 1 }); // 바로 호출되지는 않아요! 그 외에는 같아요.
bindFunc1(5, 6, 7, 8); // { x: 1 } 5 6 7 8
// 부분 적용 함수 구현
var bindFunc2 = func.bind({ x: 1 }, 4, 5); // 4와 5를 미리 적용
bindFunc2(6, 7); // { x: 1 } 4 5 6 7
bindFunc2(8, 9); // { x: 1 } 4 5 8 9

함수인자를 먼저 넣어주게 된다면 일부분만 먼저 적용 할 수 있다.
또한 바인드의 특징 다른 한가지는 name 프로퍼티를 가진다.
이때 bound라는 접두어가 붙는다. 장점은 추적이 용이하다는 것이다.
var func = function (a, b, c, d) {
console.log(this, a, b, c, d);
};
//func(1, 2, 3, 4); // window객체
// 함수에 this 미리 적용
var bindFunc1 = func.bind({ x: 1 }); // 바로 호출되지는 않아요! 그 외에는 같아요.
// bindFunc1(5, 6, 7, 8); // { x: 1 } 5 6 7 8
// 부분 적용 함수 구현
var bindFunc2 = func.bind({ x: 1 }, 4, 5); // 4와 5를 미리 적용
// bindFunc2(6, 7); // { x: 1 } 4 5 6 7
// bindFunc2(8, 9); // { x: 1 } 4 5 8 9치
var func = function (a, b, c, d) {
console.log(this, a, b, c, d);
};
var bindFunc = func.bind({ x:1 }, 4, 5);
// func와 bindFunc의 name 프로퍼티의 차이를 살펴보세요!
console.log(func.name); // func
console.log(bindFunc1.name); // bound func
console.log(bindFunc2.name); // bound func

결론은 화살표 함수를 쓰자!!