자바스크립트는 모든 것을 ‘객체’ 환경으로 이해 해야하며, 변수(함수)가 만들어진 환경의 객체를 잘 보아야 한다.
어떤 변수(함수)가 만들어진 환경이 특정 객체 안이라면, 그 함수는 그 객체의 어떤 key에 대한 value가 될 것이다. 즉, 객체에 바인딩 된 것이며, 그 객체의 메소드인 것이다.
반대로, 어떤 변수(함수)가 만들어진 환경이 전역 객체라면, 전역 객체에 바인딩 된 것이며, this는 window(브라우저 환경) || global(노드 환경)이다.
그래서 어떤 객체가 외부의 함수를 자신의 메소드로 사용하고자 한다면, 그 외부함수를 자신에게 바인딩해주어야 하는데, 다음과 같은 방법이 있다.
bind, call, apply 프로토타입 메소드
function outerFn() {
console.log(this);
}
const obj = {};
obj.a = outerFn; // 또는
obj.a = outerFn.bind(obj, ...args); // 전달인자 필요시
obj.a(); // this === obj
function printRole(user) {
console.log(user.role);
}
function transformAsHTML(array) {
const $ul = document.getElementById('container');
array.forEach(val => {
const $li = document.createElement('li');
const $a = document.createElement('a');
const $div = document.createElement('div');
$a.textContent = `${val.firstName} ${val.lastName}`;
$a.className = 'name';
// console.log(this); // window
// $a.onclick = val => printRole(val); // arrow function으로 인한 자동 this 바인딩 문제 (this === window)
$a.onclick = printRole.bind($a, val); // 디버그한 부분
$div.textContent = val.age;
$div.className = 'age';
$li.appendChild($a);
$li.appendChild($div);
$ul.appendChild($li);
});
}
익명 함수의 내부에 외부 함수를 실행
function outerFn() {
console.log(this);
}
const obj = {};
obj.a = function() { outerFn() };
obj.a(); // this === window 이지만 이로인한 버그가 일어나지 않음
이 경우 익명함수가 onclick의 값이 된다. ($a의 메소드가 된다)
function printRole(user) {
console.log(user.role);
}
function transformAsHTML(array) {
const $ul = document.getElementById('container');
array.forEach(val => {
const $li = document.createElement('li');
const $a = document.createElement('a');
const $div = document.createElement('div');
$a.textContent = `${val.firstName} ${val.lastName}`;
$a.className = 'name';
// console.log(this); // window
// $a.onclick = val => printRole(val); // arrow function으로 인한 자동 this 바인딩 문제 (this === window)
$a.onclick = function() {
printRole(val);
} // 디버그한 부분
$div.textContent = val.age;
$div.className = 'age';
$li.appendChild($a);
$li.appendChild($div);
$ul.appendChild($li);
});
}
ES6 arrow function으로 작성시 자동으로 자신이 생성된 객체의 한 단계 상위 객체에 바인딩된다.
const obj = {
a: () => { console.log(this); },
b: function() { console.log(this); },
}
obj.a(); // this === Window
obj.b(); // this === obj
또한 이후 다른 객체에 바인딩 할 수 없다.
const arrowFn = () => { console.log(this); };
const fn = function() { console.log(this); };
obj.c = arrowFn;
obj.d = fn;
obj.c(); // this === Window
obj.d(); // this === obj
// 강제 바인딩도 안됨
obj.c = arrowFn.bind(obj);
obj.c(); // this === Window