this는 함수를 호출하는 주체를 가리키고 호출주체가 없다면 this는 window를 가리킴
(브라우저 환경에서는 window, node 환경에서는 global)
둘을 나누는 기준은 '독립성'이다. 함수는 그 자체로 독립적인 기능을 수행하지만 메서드는 자신을 호출한 객체 안에서 객체에 관한 기능을 수행한다.
this는 함수에서 호출할 경우 함수를 호출한 주체가 없으므로 지정되지 않고 전역 객체(window)를 가리킨다. 메서드에서 호출하면 메서드를 호출한 객체 또는 객체 안의 프로퍼티를 가리킨다.
예외적인 부분은 객체 안의 메서드 내부라도 함수로 호출하면 this를 메서드나 객체를 가리키는 것이 아닌 전역 객체를 가리킨다. this 바인딩이 전역 객체가 아닌 어떤 호출 주체가 되려면 구문에서 호출 주체. 또는 호출 주체[]로 표기되어야 한다.
이 예외적인 부분을 우회하려면 self같은 변수에 this를 할당해주면 되지만,
var obj1 = {
outer: function() {
console.log(this);
var innerFunc1 = function() {
console.log(this);
}
innerFunc1();
var self = this;
var innerFunc2 = function() {
console.log(self);
};
innerFunc2();
}
};
obj1.outer()
위의 이러한 우회법보다는 아래처럼 call, apply, bind를 사용하여 처리하는 것이 깔끔하다.
//call을 이용
var obj = {
outer: function() {
console.log(this);
var innerFunc = function () {
console.log(this);
};
innerFunc.call(this);
}
};
obj.outer();
//bind를 이용
var obj = {
outer: function() {
console.log(this);
var innerFunc = function () {
console.log(this);
}.bind(this);
innerFunc();
}
};
obj.outer();
또 다른 예외적인 부분은 화살표 함수인데 실행 컨텍스트를 생성할 때 this 바인딩 과정이 생략되기 때문에 this는 이전의 상위값이 유지가 된다고 한다.
setTimeout 함수, forEach 메서드는 콜백 함수를 호출할 때 this의 호출대상이 지정되지 않으므로 전역 객체를 가리키게 되고 addEventListener 메서드는 콜백 함수 호출 시 자신의 this를 상속하므로 querySelector에서 어떤 버튼 요소를 선택할 경우 this는 그 버튼 요소를 가리킨다.
forEach(function(){}, this)
forEach문에서의 경우 this가 콜백 함수로 인해 (호출 주체가) 유실돼었을 때 물고 들어갈 수 있도록 위와 같은 방법을 사용한다. 이와 같이 this가 상황에 따라 계속 바뀌는데 추적하기 어렵기 때문에 명시적인 방법으로 원하는 결과를 유도할 수 있다.
const report = {
sum: 0,
count: 0,
add: function () {
const args = Array.prototype.slice.call(arguments);
//prototype이란?? : 프로토타입 기반 언어인 JS에서 기존의 객체를 복사해 새로운 객체를 생성할 때 프로토타입 객체를 참조하는 속성. prototype은 유전자라고 생각하면 편함
args.forEach(function (entry) {
this.sum += entry;
++this.count;
}, this);
},
average: function () {
return this.sum / this.count;
},
};
report.add(60, 85, 95);
console.log(report.sum, report.count, report.average());
자동으로 부여되는 상황별 this의 규칙을 깨고 call 또는 apply 메서드를 통해 내가 원하는 임의의 this 바인딩 할 수 있는 새로운 객체를 지정할 수 있다. 유사 배열 객체(length 값 필수), slice함수, Array.from 메서드 등...
this에 대해 막연히 이런 게 있다고만 알고있었는데 전보다는 조금 this의 세부적인 내용에 대해 알게 된 것 같다. 하지만 여전히 어렵고 상황에 따라 저렇게 변하는 this를 사용하는 이점이 뭔지 아직은 와닿지가 않는 것 같다.
내일은 콜백함수와 가능하면 클로저까지 공부하고 기타 궁금한 부분들도 좀 살펴보려고 한다.