# 일반함수의 this
먼저, 일반함수 내부의 this는 어떻게 동작하는지 살펴보자.
const object = {
name: '별코딩',
main: function () {
console.log(this);
},
};
object.main(); // object 객체
main이라는 메서드가 있다.
- 메서드 : 객체의 속성으로 넣어진 함수
main이라는 함수는 function키워드를 사용한 일반 함수.
이렇게 function키워드를 사용해서 정의된 일반함수의 경우에는 함수를 어떻게 호출하느냐에 따라서 this값이 바뀐다.
this는 함수를 호출한 객체가 된다. 그렇기 때문에 이 함수를 어떤 객체가 호출했느냐에 따라 this값이 달라진다.
main함수를 호출했더니, main함수 안에 this값은 object 객체가 된다.
why? → main함수를 호출한 객체는 object객체이기 때문.
어떤 객체가 호출했는지 알려면 . 왼쪽에 어떤 객체가 있는지 보면 된다.
main이라는 함수를 다른 객체의 메서드로서 호출을 하면 this값이 달라진다. 함수를 호출한 객체가 달라졌으므로.
const object = {
name: '별코딩',
main: function () {
console.log(this);
},
};
const object2 = {
name: '다른 객체',
main: object.main,
};
object2.main(); // object2 객체
object2.main함수를 호출하면, this값은 다른 객체(object2객체)가 출력된다.
똑같은 main함수를 호출했지만, main함수를 호출한 객체는 더이상 object가 아니라 object2이기 때문에 this값은 object2 객체가 된다.
코드를 조금만 변경해보자.
function main() {
console.log(this);
}
const object = {
name: '별코딩',
main: main,
};
const object2 = {
name: '다른 객체',
main: main,
};
object.main(); // object 객체 {name: '별코딩', main: f}
object2.main(); // object2 객체 {name: '다른 객체', main: f}
object객체, object2객체는 똑같은 main함수를 가지고 있다.
이렇게 똑같은 함수를 다른 객체의 메서드로서 호출하면 this값이 다르게 출력이 되는걸 볼 수 있다.
여기서 알 수 있는건,
일반 함수가 가지는 this값은 함수가 선언된 위치와는 전혀 관련이 없다.
무조건 함수가 호출된 방법에 따라서 this값이 결정된다.
main함수를 바깥에 선언을 했든, 객체 안에 선언을 했든 상관없이 this는 무조건 함수를 호출한 객체를 가리키게된다.
function 키워드를 사용한 일반 함수는 자기 자신만의 this를 갖고있기때문에, 함수가 호출되는 시점인 런타임에 this를 자신을 호출한 객체에 묶어버린다. 이런 과정을 dynamic binding, 또는 runtime binding 이라고 부르기도 한다.
왜냐하면 일반함수의 this값은 runtime, 곧 함수가 호출되는 시점에서 결정이 되기 때문.
함수가 어디서, 어떻게 선언이 됐는지는 상관이 없다.
const object = {
name: '별코딩',
main: function () {
console.log(this);
console.log(this.name);
},
};
const object2 = {
name: '다른 객체',
main: object.main,
};
object2.main(); // object2 객체
# 화살표 함수의 this
일반함수는 자신만의 this를 갖고있고, 그 this는 함수가 호출된 시점에서 결정이 된다.
함수를 호출한 객체를 가리키도록.
하지만 화살표함수는 자신만의 this를 가지고 있지 않다.
그래서 화살표함수 내부에서 this에 접근을 하면 외부로부터 this를 가져와서 사용하게 된다.
const object = {
name: '별코딩',
main: function () {
console.log(this);
},
mainArrow: () => {
console.log(this);
}
};
object.mainArrow(); // Window객체
화살표함수는 자신을 감싸고 있는 scope로 올라가서 this를 찾게된다. 그렇게 찾은 this를 그대로 가져와서 사용하게된다.
위 코드에서 화살표함수가 어떤 this값을 가질지 궁금하면 함수가 선언된 자리에 this를 넣어서 확인해보자.
const object = {
name: '별코딩',
main: function () {
console.log(this);
},
mainArrow: this,
};
console.log(object.mainArrow); // Window 객체
그래서 원래 this자리에 있던 화살표 함수는 window객체를 그대로 가져와서 this값으로 사용하게 된다.
화살표함수의 this값은 함수가 선언된 위치에서 결정이 난다. 왜냐하면 화살표함수를 감싸는 scope의 this를 가져다가 쓰기 때문.
그렇기 때문에 화살표함수를 어떻게 호출했든지 상관없이 화살표함수가 선언된 시점에서 this가 결정이 되고 더이상 바뀌지 않는다.
일반함수와는 반대로 작동한다.
- 일반함수 : 함수가 어디서 선언됐는지 상관없이 '함수가 호출된 방법'에 따라서 this가 바뀐다.
- 화살표함수 : '함수가 호출된 방법'은 상관없이 '함수가 어디서 선언됐는지' 중요.
const object = {
name: '별코딩',
main: function () {
console.log(this);
},
mainArrow: () => {
console.log(this);
}
};
const object2 = {
name: '다른 객체',
mainArrow: object.mainArrow,
};
object.mainArrow(); //window 객체
object2.mainArrow(); //window 객체
이렇게 다른 객체(object2객체)가 mainArrow함수를 호출해도 this값은 변경되지 않는다.
정리하면,
화살표함수의 this는 함수가 선언된 위치에 의해서 결정이 된다.
함수가 호출된 방법과는 무관하다.
화살표함수를 객체의 메서드로 사용하는건 좋은 방법일까?
→ 그렇지 않을 때가 많다.
메서드 내부에서 this를 통해 객체의 속성에 접근해야될 때가 있다.
하지만 화살표함수의 경우, this는 더이상 object 객체가 되지 않는다. 그래서 "this.name" 이렇게 우리가 만든 object객체의 속성에 더이상 접근을 할 수 없게 된다.
그래서 객체의 메서드로는 화살표함수보다는 일반함수를 사용하는게 더 적합하다.
# 예제1 - 내장함수
- 내장함수 : 함수 안에 들어있는 함수
## 일반함수인 내장함수
const object = {
name: '별코딩',
main: function () {
const innerFunction = function () { //함수 안에 들어있는 내장함수
console.log(this);
};
innerFunction();
},
};
object.main();
main함수를 호출하면 main함수가 실행이 되면서 innerFunction함수도 같이 호출이 된다.
main함수를 호출하면 innerFunction함수 내부의 this는 무슨 값이 될까?
→ window객체가 된다.
왜 우리가 만들어준 object객체가 아니라 window객체가 되는걸까?
일반함수의 this는 함수를 호출한 객체가 된다.
innerFunction함수의 호출문을 보면, 어떤 객체로부터 직접적으로 호출이 된게 아닌걸 볼 수 있다.
그렇기 때문에 innerFunction안에 있는 this값은 자동으로 window객체가 된다.
## 화살표함수인 내장함수
이번에는 innerFunction을 일반함수가 아니라, 화살표함수로 바꿔보자.
const object = {
name: '별코딩',
main: function () {
const innerFunction = () => {
console.log(this);
};
innerFunction();
},
};
object.main(); // object객체
실행을 해보니, this는 우리가 만든 object 객체가 된다.
왜그런걸까?
화살표함수의 this는 화살표함수를 감싸는 외부 scope의 this를 그대로 가져와서 사용한다.
화살표함수를 감싸고 있는건 main함수이다.
이 main함수가 실행이 될때 main함수 안에 있는 this값을 무엇이 될까? → object객체
왜냐하면 object.main()이므로 main함수를 object객체가 호출을 했기 때문.
화살표함수는 main함수가 가지고 있는 this값을 그대로 가져와서 사용을 하게된다.
그렇기 때문에 이 this값은 object 객체가 된다.
찾기 어렵다면, 화살표함수를 감싸고 있는 scope에 console.log(this)로 this값을 출력해보면 된다.
const object = {
name: '별코딩',
main: function () {
console.log(this);
const innerFunction = () => {
console.log(this);
};
innerFunction();
},
};
object.main();
## 일반함수인 내장함수에 bind 사용 - 가능
일반함수의 경우에는, this값을 우리가 원하는 객체로 고정을 하고싶으면 bind함수를 사용하면 된다.
함수 뒤에 ".bind"를 작성하여 우리가 원하는 객체를 안에 넣어주면 this값이 우리가 원하는 객체로 고정된다.
이렇게 bind를 사용해서 우리가 원하는 객체를 넣어주면 함수가 어떻게 호출되든지 상관없이 this를 우리가 원하는 객체로 고정해줄수 있다.
const object = {
name: '별코딩',
main: function () {
console.log(this);
const innerFunction = function () {
console.log(this);
}.bind({hi: 'hi'});
innerFunction();
},
};
object.main();
## 화살표함수인 내장함수에 bind 사용 - 불가능
하지만, 화살표함수는 그게 불가능하다.
bind를 이용해서 this를 우리가 원하는 객체로 바꿔줄 수 없다.
왜냐하면 화살표함수는 bind를 해줄 자신만의 this를 갖고있지 않기 때문.
const object = {
name: '별코딩',
main: function () {
const innerFunction = (() => {
console.log(this);
}).bind({hi: 'hi'});
innerFunction();
},
};
object.main(); // object객체
실행시키면, this값이 여전히 object객체인걸 볼 수 있다.
화살표함수의 this값은 화살표함수가 정의되는 시점에서 정의되는 위치에 의해 결정이 되고, 이후에는 변경되지 않기 때문.
그래서 bind를 해주는 의미가 없다. bind를 하더라도 그냥 무시해버린다.
bind를 해줬음에도 불구하고 this는 아직도 object 객체.
# 예제2 - 콜백함수
## 일반함수인 콜백함수
콜백함수로는 function키워드를 이용한 일반함수를 넣어보자.
const object = {
name: '별코딩',
main: function () {
setTimeout(function(){
console.log(this)
}, 1000)
},
};
object.main();
1초 후, window객체가 출력되는걸 볼 수 있다.
인자로 넣어준 콜백함수는 setTimeout이 내부적으로 알아서 호출을 해준다.
그렇기때문에 콜백함수를 어떻게 호출할지는 setTimeout함수의 내부적인 구현사항에 달려있다.
그 말은, 우리가 콜백함수로 넣어준 함수의 this는 setTiemout함수의 내부적인 구현사항에 달려있다는 뜻.
왜냐하면 콜백함수는 일반함수이기때문에 함수를 호출하는 방법에 따라 this값이 달라질 수 있고,
혹은 setTiemout이라는 함수 내부적으로 bind를 사용해서 우리가 전달해준 이 함수를 우리가 모르는 또다른 객체를 this에 바인딩 시켜줄 수 있기 때문.
setTimeout이라는 함수는 브라우저에서 제공해주는 API이기때문에 우리가 만든 함수가 아니다.
setTImeout 함수에는 우리가 전달해준 함수의 this가 window가 된다.
## 화살표함수인 콜백함수
이번에는 콜백함수로 일반함수가 아닌 화살표함수를 전달해보자.
const object = {
name: '별코딩',
main: function () {
setTimeout(() => {
console.log(this)
}, 1000)
},
};
object.main();
this값으로 object 객체가 나온다.
왜그런걸까?
화살표함수의 this는 외부로부터 가져온다. 그리고 this는 화살표함수가 선언된 위치에서 결정이 된다.
그 이후에는 변하지 않는다.
화살표함수를 잠시만 지우고, this로 교체해서 생각해보자.
여기 있는 this는 main함수에 들어갈 this가 된다.
const object = {
name: '별코딩',
main: function () {
console.log(this); //object객체
setTimeout(this, 1000)
},
};
object.main();
그래서 화살표함수의 this는 외부 scope인 main함수의 this를 가져와서 사용한다.
화살표함수의 this는 함수를 호출하는 방법에 따라 변하지 않는다.
그렇기 때문에 setTimeout의 내부구현사항을 신경쓰지 않는다.
# 정리
** 출처: 별코딩-"화살표함수"강의를 기반으로 작성하였습니다.
'JavaScript' 카테고리의 다른 글
표현식과 문 (0) | 2023.09.21 |
---|---|
스코프 의미, 규칙 및 종류 (2) | 2023.09.18 |
자바스크립트 ES6모듈 내보내기/불러오기(export/import) (0) | 2023.08.16 |
함수 호출 방식에 의해 결정되는 this - 1. 함수 호출 (0) | 2023.07.05 |
이벤트 리스너(event listener) (0) | 2023.06.29 |