this 는 호출자를 가르킨다.
this를 활용?하는 5가지의 경우
1.전역공간에서 (window/global 전역객체)
2.함수내부에서 (window/global 기본적으로는 전역객체이며 바인딩으로 변경할수 있다.)
3.메소드 호출시 (메소드 호출한 주체(메소드명 .앞까지))
4.callback에서 (기본적으로는 함수내부와 동일 단 call,apply,bind 호출로 변경할수 있음)
5.생성자함수에서 (인스턴스)
전역공간 에서의 this는 전역객체이다.
함수 내부에서 this는 기본적으로 전역객체이다.
function a() {
console.log(this);
function b() {
console.log(this);
}
b();
}
a(); //window window
기본적인 함수에서 this는 전역객체이며 외부 내부 함수에서도 전역객체이다.
call,apply,bind는 아래에서 정리
메서드에서 this는 메서드를 호출한 주체이다.
let a = {
b: function() {
console.log(this);
}
};
a.b(); // 객체 a
//////////////////////////////////
let a = {
b: {
c: function() {
console.log(this);
}
}
};
a.b.c(); // 객체 b반환
a.b() 에서 this 는 객체 a를
a.b.c() 에서 this는 객체 b를 가리킨다.
기본적으로는 함수내부에서와 동일하지만 call,apply,bind 메서드를 통해 this를 변경할수있다.
call,apply,bind는 함수를 호출하는 메서드이다. MDN에서 call,apply,bind 구문이다.
fun.call(thisArg[, arg1[, arg2[, ...]]])
fun.apply(thisArg, [argsArray])
func.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg는 설정해줄 this는 전달해주는 매개변수이다.
call과 bind는 두 번째 매개변수로 함수에 전달해줄 인자들을 일일이 적어준다.
apply는 배열로 묶어서 한 번에 전달해준다.
call과 apply는 즉시 호출하는 메서드이며 bind는 새로운 함수를 생성할 뿐 즉시 호출은 아니다
function a(x,y,z) {
console.log(this,x,y,z)
}
let b = {
c: 'obj'
}
a.call(b,1,2,3)//{c: "obj"} 1 2 3
a.apply(b,[1,2,3])//{c: "obj"} 1 2 3
let c = a.bind(b,1,2,3)
c()//{c: "obj"} 1 2 3
위에 코드에서 a(1,2,3) 호출하면 window 1 2 3 이 출력될 것이다.
하지만 call과 apply 메서드로 각각 this 값에 b를 담았고 그 결과 출력은 {c: "obj"} 1 2 3로 둘 다 똑같이 출력된다.
bind는 즉시 호출이 아니기 때문에 변수에 담아서 호출하면 {c: "obj"} 1 2 3 같은 값을 출력한다.
다시 callback this로 돌아와서
function callback() {
console.log(this);
}
let obj = {
a: function(cb) {
cb(); //현재 this는 window
}
};
obj.a(callback);
위 코드에서 callback 함수의 this는 window이다.
cb()
코드를 cb.call(this)
로 바꿔주면 this는 obj가 된다. 그 이유는 콜백 함수의 제어권을 갖고 있는 메서드 a()의 this는 obj이고 call 이란 메서드를 통해 다시 this를 넘겨줌으로 window가아닌 obj가 된다.
function callback() {
console.log(this);
}
let obj = {
a: 1
};
setTimeout(callback.bind(obj), 100);
setTimeout 은 this에 대한 별도의 처리가 없기 때문에 window이다. 앞에 코드와 달리
setTimeout의 콜백 처리를 변경할 수 없기 때문에
(cb() -> cb.call(this)
로 호출을 변경 한 것처럼)
callback.bind(obj)로 처리한다.
bind를 사용한 이유는 call과 apply는 즉시 호출이기 때문에 조건에 따라 실행되는 이벤트 함수에서는 bind를 통해 함수로 넘겨준다.
이벤트 리스너는 또 달라진다.
//버튼 하나 만들고
document.querySelector("body").prepend(document.createElement("button"));
document.querySelector("button").addEventListener("click", function() {
console.log(this); //<button></button>
});
해당 코드의 this는 으로 이벤트 리스너 함수는 내부적으로 this를 DOM 객체를 가리키도록 설정한다.
여기서 조금더 신경 쓰자면
//버튼생략
document.querySelector("button").addEventListener("click", function() {
console.log(this);
function inner() {
console.log(this);
}
inner(); //window
});
콜백 함수 내에서 선언된 inner 함수는 또 기본 원칙에 따라 this는 window 이다.
이 문제를 해결하기 위해서는
스코프 체인을 이용해서 해결해주거나 화살표 함수를 이용해 해결해준다.
스코프체인
//버튼생략
document.querySelector("button").addEventListener("click", function() {
console.log(this);
let self = this;
function inner() {
console.log(self);
}
inner();
});
화살표함수
//버튼생략
document.querySelector("button").addEventListener("click", function() {
console.log(this);
let inner = () => {
console.log(this);
};
inner();
});
화살표 함수에서 this는 상위 함수에 this를 따른다.
this의 변화를 굵직하게 정리했지만 이벤트 리스너 함수에서처럼 내부적으로 this를 다르게 정의하는 경우가 있다고
한다. 라이브러리를 사용할 때 this를 항상 체크해야 한다 (아직은 뭐... 와닿지 않는다..)
이부분은 전에 생성자 함수 정리하면서 겪어봤던 부분이다.
function Person(a,b) {
this.name = a
this.age = b
}
let a = new Person('dabin',28)
여기서 this는 인스턴스(코드에서는 a)를 가리킨다.