this
는 this
가 실행되는 함수가 포함되어져 있는 객체를 지칭한다.
그래서 this
가 bradley
로 바뀌어도 전혀 상관없지만 bradley
라는 이름은 충분히 바뀔 수 있는 데이터이기 때문에 this
로 사용한다.
(또한, 객체 데이터가 또 다른 객체에 할당될 수도 있다.)
const bradley = {
name: 'minyoung',
normal : function () {
console.log(this.name)
},
arrow: () => {
console.log(this.name)
}
}
bradley.normal() // minyoung
bradley.arrow() // undefined
일반 함수
에서는 호출 위치에 따라 this
를 정의한다.
화살표 함수
는 자신이 선언된 함수 범위에서 this
를 정의한다. 호출 위치와 전혀 상관없다.
선언될 때 this
가 무엇인지 충분히 알 수 있다.
로직 환경에 따라 다양한 부분을 지칭할 수 있다. 위 로직에서는 name
을 지칭하는 부분이 없기에 undefined
로 반환된다.
화살표함수
는 함수가 만들어지는 위치에서 정의된다는 것을 기억하자.
const rachael = {
name: 'kayeon',
normal: bradley.normal,
arrow: bradley.arrow
}
// rachael.normal() // kayeon
// rachael.arrow() // undefined
위 함수에서 normal
, arrow
는 중괄호가 따로 없기 떄문에 호출되는 함수가 아니다. rachael
의 normal
에 bradley.normal
함수가 할당된다.
rachael.arrow
역시 화살표함수이기에 호출된 위치와 상관없이 똑같이 undefined
가 나오게 된다.
const timer = {
name: 'bradley',
timeout: function(){
setTimeout(function () {
console.log(this.name)
}, 2000))
}
}
timer.timeout() //undefined
setTimeout
함수는 인자를 두 개 받는다. (콜백함수, 시간) 위 코드를 실행해보면,
콘솔창에 undefined
가 뜨게 된다.
setTimeout
의 콜백함수 내부의 콘솔의 this
는 일반함수
로 만들어져 있으며, 일반함수
는 호출 위치에서 this
가 정의된다. 그래서 this
는 setTimeout
함수의 어딘가에서 실행이 될 것이며, timer
객체의 name
으로서 this
가 실행되길 원했다면 화살표 함수
로 만들어주어야 한다.
const timer = {
name: 'bradley',
timeout: function(){
setTimeout(() => {
console.log(this.name)
}, 2000)
}
}
timer.timeout() //bradley
그러면 결과값으로 bradley
가 나오게 된다.
화살표함수
를 감싸고 있는 timeout
이라는 함수 범위로 this
가 정의된다.
또한 timeout
이라는 일반함수
는 timer
라는 객체 데이터를 가리킨다.
그렇기 때문에, 결국 this
는 timer
가 된다. 그래서 화살표함수
를 써서
자신이 선언된 함수 범위에 this
를 주로 정의하고 사용한다.
일반함수와 화살표함수를 제대로 이해하고 있어야 자바스크립트 클래스에서
this
를 일반함수
로 만들지 화살표함수
로 만들지 명확하게 결정할 수 있다.
THIS 추가 내용
(인프런 "코딩인터뷰를 저격하는 JS 스나이퍼 양성학교" 강의 참조)
this가 존재하는 이유
var myDinner = {
name : "김치찌개",
menu : function() {
console.log("오늘 저녁은 " + myDinner.name)
}
}
myDinner.menu();
위와 같은 객체에서, menu함수는 myDinner변수의 이름이 수정될 경우나
menu 함수 자체를 다른 객체에서 사용하고 싶을 경우, 사용이 불편하다.
myDinner가 아니라 yourDinner가 된다면,
console.log 내의 myDinner도 yourDinner로 바뀌어야한다.
menu의 value 함수만 따로 빼서 쓰더라도 그 객체에서 myDinner를 가지고 있는지 알 수 없다.
그렇기 떄문에 함수를 재사용하기가 매우 힘들어진다.
this를 통해 함수를 재사용해보자.
어떤 객체에서도 사용할 수 있는 글로벌 함수를 만들어보자.
function menuGlobal() {
console.log("오늘 저녁은" + this.name);
}
var myDinner = {
name: "김치찌개",
menu: menuGlobal
}
myDinner.menu(); // "오늘 저녁은 김치찌개"
myDinner라는 객체 내에서 호출된 menuGlobal이라는 함수가 자신을 호출한 객체인 this.name 을 가져오는 것을 볼 수 있다.
myDinner가 아닌 다른 객체를 만들어 보자.
var yourDinner = {
name : "된장찌개",
menu : menuGlobal
}
yourDinner.menu(); // "오늘 저녁은 된장찌개"
이렇게 다른 객체에서도 재사용 가능한 함수가 되었다. 이게 this의 핵심적인 기능이다.
"this를 이용하면 함수를 다른 객체에서도 재사용할 수 있다"
이제 this를 제어할 수 있어야 한다.
(일반적으로 this의 값은 자동으로 할당되지만, 상황에 따라 제어할 수 있어야 한다.)
this를 제어하는 함수를 소개한다.
1. call()
- call 메서드는 this의 값을 바꿀 수도 있고, 함수를 실행할 때 사용할 인수도 전달할 수 있다.
function menuGlobal(item) {
console.log("오늘 저녁은 " + item + this.name);
}
var myDinner = {
name: "김치찌개"
}
var yourDinner = {
name: "된장찌개"
}
// menuGlobal함수에 call 메소드를 사용한다.
// call의 첫번째 인수로 함수에 this를 바라볼 객체를 넣어준다.(지정)
menuGlobal.call(myDinner, "묵은지"); // 오늘 저녁은 묵은지김치찌개
// this는 myDinner를 바라보게 된다.
// 그리고 두번째 인자로 item에 해당하는 인자를 전달한다.
menuGlobal.call(yourDinner, "차돌"); // 오늘 저녁은 차돌된장찌개
// menuGlobal함수가 콜을 한다. yourDinner라는 객체를, item에 차돌을 넣어서.
2. apply()
- apply메서드는 함수를 실행할 때 배열로 묶어 한번에 전달합니다.
function menuGlobal(item1,item2){
[item1,item2].forEach(function(el){
console.log("오늘 저녁은" + el + this.name)
}, this);
}
var myDinner = {
name: "김치찌개"
}
var yourDinner = {
name : "된장찌개"
}
menuGlobal.apply(myDinner, [" 묵은지 ", " 우삼겹 "]); //"오늘 저녁은 묵은지 김치찌개", "오늘 저녁은 우삼겹 김치찌개"
menuGlobal.apply(yourDinner, [" 두부 ", " 애호박 "]); //"오늘 저녁은 두부 된장찌개", "오늘 저녁은 애호박 된장찌개"
- call()과 apply()의 차이
call은 함수를 실행할 때 전달할 인수를 하나 하나 전달한다면 (여러 개의 인수 전달 가능)
apply는 전달할 인수를 배열로 묶어 한번에 전달한다.
그래서 인수를 두 개만 사용한다. (this를 지정할 객체, 전달할 인수 배열)
인수를 배열로 보낸다는 점 빼고 call과 apply는 동일한 기능을 수행한다.
3. bind()
- es5에서 추가되었다.
- this값을 어디서 사용하든 호출 객체가 바뀌지 않게 고정시켜버린다.
function menuGlobal(item){
console.log("오늘 저녁은" + item + this.name)
}
var myDinner = {
name: "김치찌개"
}
var yourDinner = {
name : "된장찌개"
}
var menuGlobalForMe = menuGlobal.bind(myDinner);
- menuGlobalForMe 변수에는 menuGlobal 함수가
myDinner를 바라보는 새로운 고정된 함수가 들어가게 된다.
console.log(menuGlobalForMe("묵은지")); // "오늘 저녁은묵은지김치찌개"
menuGlobal 함수의 this값은 myDinner가 된다.
var menuGlobalForYou = menuGlobal.bind(yourDinner);
console.log(menuGlobalForYou("우삼겹"));
그렇다면, bind()는 이렇게도 사용이 가능하다.
myDinner객체에 새로운 키값 menuMine 이라는 키를 넣고 값을 menuGlobalForYou를 넣는다면,
myDinner.menuMine = menuGlobalForYou;
위 코드는 아래와 같다.
var myDinner = {
name: "김치찌개",
menuMine: menuGlobalForYou
}
그렇게되면, myDinner.menuMine()을 함수로 실행할 수 있게 된다.
실행하면,
console.log(myDinner.menuMine("묵은지")); // "오늘 저녁은 묵은지 된장찌개"
myDinner에서 사용하지만, 바라보는 객체는 yourDinner이다.
# 화살표 함수와 this
- 화살표 함수의 This는 일반적인 This처럼 함수를 호출한 객체를 할당하지 않고,
바로 상위 스코프의 This에 할당하게 된다.
- apply에서 확인했던 코드를 다시 가져와보겠다.
function menuGlobal(item1,item2){
[item1,item2].forEach(function (el) {
console.log("오늘 저녁은" + el + this.name)
}, this);
}
여기서, forEach문에서 두 번째 인수 this가 없으면
콘솔 내 this는 자신을 호출하는 함수를 불러오는 객체인 window를 바라보게 된다.
그래서 this를 써주어야 하지만, 화살표 함수를 사용하게 된다면,
두번 째 인수를 비워주어도 된다. 그렇다면 상위 스코프의 this는 무엇일까?
function menuGlobal(item1,item2){
console.log(this);
[item1,item2].forEach((el) => {
console.log("오늘 저녁은" + el + this.name)
});
}
menuGlobal.apply(myDinner, [" 묵은지 ", " 우삼겹 "]);
// [object Object] {
name: "김치찌개"
}
// "오늘 저녁은 묵은지 김치찌개"
// "오늘 저녁은 우삼겹 김치찌개"
둘 다 김치찌개로 나온다. 상위 스코프의 this는 myDinner를 바라보고 있다.
화살표 함수내의 this는 상위 스코프의 this를 바라본다.
[정리]
forEach 내부의 this는 전역객체를 바라보고 있다.
화살표 함수를 만들어줌으로써, this는 상위 스코프를 바라보게된다.
- this는 함수를 호출하는 객체를 의미한다. (객체.a에서 this는 객체)
- call, apply는 this에 할당되는 객체를 지정할 수 있다.
- Bind 메서드는 this에 할당되는 객체가 고정된 새로운 함수를 생성한다.
- 화살표 함수에서의 this는 상위 스코프의 객체를 할당받는다.
- this는 함수가 호출될 때 결정된다.
- 화살표함수는 bind() 메서드 사용 불가.
- 일반함수가 호출한 this는 전역객체, 화살표함수가 호출한 this는 한 단계 위 콘텍스트의 this를 가리킨다.
- 메소드가 일반함수면 this는 '.'앞의 객체, 화살표 함수면 window.