[JavaScript] 스터디 14일차_3강 this 와 화살표 함수
this와 화살표 함수(arrow function)
ES6에서 도입된 화살표함수는 스스로의 this 바인딩을 제공하지 않는 화살표 함수를 추가했다.
나는 화살표 함수가 단순히 함수를 "짧게" 쓰기 위한 용도인 줄 알았는데, 화살표 함수와 this는 밀접한 관련이 있다.
화살표 함수는(arrow function expression)은 function 표현에 비해 구문이 짧고, 자신의 this, arguments, super 또는 new.taget을 바인딩하지 않는다. 화살표 함수는 항상 익명이다. 이 함수 표현은 메소드 함수가 아닌 곳에 가장 적합하다. 그래서 생성자로 사용할 수 없다. (mdn)
여기서 우리는 화살표 함수의 arguments와 super, new.target을 바인딩하는 것은 다음에 알아보기로 하고, this를 바인딩하지 않는 것만 보겠다😊 (절대 귀찮은 것이 아니다)
화살표 함수가 나오기 전까지, 모든 새로운 함수는 어떻게 그 함수가 호출되는지에 따라 자신의 this값을 정의했다.
즉 this는 동적으로 결정되는 것이었다.
(스터디할때 했었쥬? 생략 (❁´◡`❁))
호출하는 것에 따라 this가 바뀌는 것은 객체 지향 스타일로 프로그래밍할때 좋지 않다.
function Person() {
// Person() 생성자는 `this`를 자신의 인스턴스로 정의.
this.age = 0;
setInterval(function growUp() {
// 비엄격 모드에서, growUp() 함수는 `this`를
// 전역 객체로 정의하고, 이는 Person() 생성자에
// 정의된 `this`와 다름.
this.age++;
}, 1000);
}
var p = new Person();
이를 해결하기 위해 ES6 이전에는 this를 변수에 할당하여 우회하는 방식으로 this를 세팅해주었다.
이렇게 하면 p를 호출할때 age값이 1,2,3 ... 증가하는 것을 볼 수 있다.
왜? 이제 this는 내가 지정한 Person의 this를 가리키기 때문!
function Person() {
var that = this; // this를 that이라는 변수에 할당, 우리는 스터디에 self에 할당했었지유?
that.age = 0;
setInterval(function growUp() {
// 콜백은 `that` 변수를 참조하고 이것은 값이 기대한 객체이다.
that.age++;
}, 1000);
}
화살표 함수에는 this가 없다
금일 스터디는 마무리 하는 것으로....
✨화살표 함수는 자신의 this가 없다... 대신 화살표 함수를 둘러싸는 렉시컬 범위(lexical scope)의 this가 사용된다. 화살표 함수는 일반 변수 조회 규칙(normal variable lookup rules) 때문에 현재 범위에서 존재하지 않는 this를 찾을 때,
화살표 함수는 바로 바깥 범위에서 this를 찾는것으로 검색을 끝낸다. 즉 화살표 함수에서는 this에 바인딩할 객체가 정적으로 결정된다.
일반 함수와 달리 화살표 함수의 this가 언제나 상위 스코프의 this를 가리키는 것을 Lexical this라고 한다.
setInterval 함수를 화살표 함수로 바꾸었다. 화살표 함수의 this는 상위 함수인 Person의 this와 동일한 값을 갖는다.
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this|는 Person 객체를 참조
}, 1000);
}
var p = new Person();
화살표 함수를 써서 좋은 다른 예제를 보자
const food = {
name: "amy favorite",
foods: ["dduckbboki", "cake", "pizza"],
mouthDiet(){
this.foods.forEach(
food => console.log(this.name + "은 " + food + "이다")
);
}
};
food.mouthDiet();
출력 결과는 의도한 대로 아주 잘 나왔다. forEach 메서드에서 화살표 함수를 쓰면서 this가 화살표 함수 바깥의 this를 가리킬 수 있었기 때문이다. 그래서 amy favorite을 잘 가져왔다.
만약 이것을 일반 함수를 사용했다면 어떻게 되었을까?
const food = {
name: "amy favorite",
foods: ["dduckbboki", "cake", "pizza"],
mouthDiet(){
this.foods.forEach(function(food) {
console.log(this.name + "은 " + food + "이다")
});
}
};
food.mouthDiet();
출력결과를 확인하자 this.name인 "amy favorite"을 가져오지 못한다. 사실상 undefined 이다. 여기서 this를 console.log로 찍어보면 window가 나온다.
내부 함수에서 this는 전역객체를 가리킨다. 브라우저에서 확인했기 때문에 window가 찍힌다.
그렇다면, 객체의 메소드에 화살표를 써도 될까???
diet 객체 내부의 food 라는 메소드에 화살표 함수를 썼다. 원래 메소드는 점 표기법에 의해 diet의 this를 가리켜야한다. but 화살표 함수는 바깥 스코프에서 this를 찾기 떄문에 diet를 넘어가 this는 전역객체인 window로 정해져버린다.
전역객체에는 name이라는 변수가 없다. 그리하여 아래 예시에서 this.name이 undefined를 가리켜버린다.
그렇기 때문에 메서드에는 일반 function 함수를 써주어야한다.
const diet = {
name: "hyemi",
food: () => console.log(`${this.name}아 또 뭐먹었니?`)
};
diet.food(); // 아 또 뭐 먹었니?
오호? 또 궁금증이 생겼다. ( •̀ ω •́ )✧ (그..그만)
화살표 함수는 call, apply, bind 메서드로 바인딩을 할 수 있을까?
정답은 No!!!!! 이미 정적으로 결정되어있는 this를 세개의 메소드로 다시 지정해줄 수 없다.
아래 예제에서 기본 함수를 쓴 console.log는 this가 버섯으로 잘 바인딩 되었다.
but 화살표 함수를 쓴 console.log의 결과는 window(상위 스코프)를 가리킨다.
function vegetable(mushroom){
mushroom.call("버섯");
};
vegetable(function(){
console.log(this);
})
vegetable(()=>{
console.log(this);
})
출력결과
참고자료
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/Arrow_functions