오늘은 계속 미루고 미뤘던 화살표함수에 대해 정리하려고 한다. 클론코딩을 하면서 화살표함수를 따라서 쓰긴 하는데 정확하게 알고 있지 않고 쓰니까 찜찜한 기분이라서 정리하고 쓰도록 해야겠다는 생각이 들었다:)
그래서,
: es6부터 나온 함수로 function(){} 함수의 형태를 간단하게 표현한 함수이면서 몇 가지의 특징이 있는 함수.
형태는
() => {}
의 모습을 가지고 있고,
// 매개변수 지정 방법
() => {} // 매개변수가 없을 경우
x => {} // 매개변수가 한 개인 경우, 소괄호 생략가능
(x, y) => {} // 매개변수가 여러 개인 경우, 소괄호 생략 불가능
// 함수 몸체 지정 방법
x => {return x * x} // single line block
x => x * x // 함수 몸체가 한줄의 구문이라면 중괄호를 생략할 수 있고 암묵적으로 return이 되어서 return을 쓰지 않아도 된다.
() => {return {a: 1};}
() => ({a: 1}) // 위의 표현과 동일. 객체 반환시 소괄호 사용
() => { // multi line block
const x = 10;
return x * x;
};
화살표함수는 익명함수로만 사용할 수 있어서 "함수 표현식"을 사용해야한다.
ex)
const add = (x, y) => x + y;
console.log(add(2, 3)); // 5
그리고 중요한 사실!
❗️❗️콜백함수로 일반적인 함수 표현식보다 간결하게 사용할 수 있다.
const arr = [1, 2, 3];
const pow = pow.map(function (x) {return x * x;});
// es5에서는 이렇게 써야 했지만,
const pow = pow.map(x => x * x);
// es6부터는 이렇게 간단하게 써도 된다.
console.log(pow); // [1, 4, 9];
💡 화살표함수의 특징
1. 익명함수로만 만들 수 있다
2. 생성자함수로 사용이 불가능하다 (무거운 프로토타입을 만들지 않는다)
3. 함수 자체 arguments(인자에 대해 모든 정보를 알고 있는 객체)를 가지고 있지 않다.
4. this에 대한 바인딩이 정적으로 결정된다. (함수에서 제일 근접한 상위 스코프의 this에 정적으로 바인딩된다)
5. call, apply, bind 메소드를 사용하여 this를 변경할 수 없다.
일반함수와 화살표함수의 가장 큰 차이점은 this이다.
자바스크립트에서 함수를 호출할 때 this는 동적으로 결정된다. 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정되는 것이 아니라 함수를 호출할 때, 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정된다.
콜백함수의 this는 전역객체 window이다.
화살표함수는 함수를 선언할때 this에 바인딩할 객체가 정적으로 결정된다. 동적으로 결정되는 일반함수와는 달리 화살표함수의 this는 언제나 상위 스코프 this를 가리킨다. (Lexcial this) 그리고 call, apply, bind메소드를 사용하여 this를 변경할 수 없다.
💡화살표함수에는 this와 argument가 없기 때문에 일반적으로 this가 개입되는 경우라면 일반함수를 사용해야한다.
let obj = {
myName: 'Son',
logName: function() {
console.log(this.myName);
}
};
obj.logName(); //"Son"
// 호출한 객체에 this가 바인딩 되므로 Son이 출력되는 것 확인
var obj = {
myName: 'Son',
logName: () => {
console.log(this.myName);
}
};
obj.logName(); //undefined
/*콜백함수로 화살표함수를 사용하면 이 예제의 경우 this는 상위 스코프인 전역스코프 혹은 window객체.
현재 상위 스코프에는 변수 myName이 존재하지 않으므로 undefined를 반환*/
const person = {
name: 'Lee',
sayHi: () => console.log(`Hi ${this.name}`);
};
person.sayHi(); // Hi undefined
위의 코드를 보면 메소드로 정의한 화살표 함수 내부의 this는 상위컨택스트인 전역객체 window가 되므로 화살표함수로 메소드를 정의하는 것은 바람직하지 않다.
이와 같은 경우에는 메소드를 위한 단축 표기법 '축약 메소드 표현'을 사용하는 것이 좋다.
const person = {
name: 'Lee',
sayHi() {
console.log(`Hi ${this.name}`);
}
};
person.sayHi(); // Hi Lee
const person = {
name: 'Lee',
};
Object.prototype.sayHi = () =>. console.log(`Hi ${this.name}`);
person.sayHi(); // Hi undefined
화살표함수로 객체의 메소드를 위처럼 정의하면 문제가 발생한다.
프로토타입을 가지지 않으니까!
const person = {
name: 'Lee',
}
Object.prototype.sayHi = function () {
console.log(`Hi ${this.name}`);.
};
person.sayHi(); // Hi Lee
const Foo = () => {};
console.log(Foo.hasOwnProperty('prototype')); // false
const foo = new Foo() // TypeError : Foo is not a constructor
화살표함수는 생성자함수로 사용할 수 없다. 생성자 함수는 prototype 프로퍼티를 가지며 prototype 프로퍼티가 가리키는 프로토타입 객체의 constructor을 사용한다. 하지만 화살표함수는 위에 언급한 것처럼 prototype 프로퍼티를 가지고 있지 않다.
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // true
this.innerHTML = `Clicked button`;
});
const button = document.getElementById('mybutton');
button.addEventListener('click', function() {
console.log(this === button); // true
this.innerHTML = 'Clicked button';.
});
addEventListener 함수의 콜백함수를 화살표 함수로 정의하면 this가 상위 컨택스트인 전역객체 window를 가리킨다. 따라서 addEventListener함수의 콜백함수 내에서 this를 사용하는 경우, function키워드로 정의한 일반함수를 사용해야한다. 일반함수로 정의된 addEventListener함수의 콜백함수 내부의 this는 이벤트 리스너에 바인딩된 요소(currentTarget)를 가리킨다.
🧑🏻 :
Why doesn't my arrow function work in my addEventListener?
I created this arrow function in app.js but ran into an error:
"Uncaught TypeError: Cannot set property 'display' of undefined"
button.addEventListener('click', () => {
game.startGame()
this.style.display = 'none';
document.getElementById('play-area').style.opacity = '1';
});
I double checked my code and the block was the same as the solution just not as an arrow function. Tried it as below and it worked:
button.addEventListener('click', function(){
game.startGame()
this.style.display = 'none';
document.getElementById('play-area').style.opacity = '1';
});
Did I declare my arrow function wrong? Or is it not possible to use arrow functions in instances like this?
Thanks!
👱 :
Hi Sean,
Arrow functions do not have their own this. It doesn't bind this like the function keyword does, so yes, that it why this is undefined in the first code example.
In this case you'll want to use the function keyword, or you write your callback to accept the event parameter, like so:
button.addEventListener('click', (evt) => {
game.startGame()
evt.target.style.display = 'none';
document.getElementById('play-area').style.opacity = '1';
});
In this case, evt is the click event, and the target is the button that receives the event. As such you can change the style for the button that way.
Hope that helps. Great question! :)
클론코딩을 하면서 addEventListener함수에 화살표함수를 쓰는 것을 보고, 위처럼 정리한 내용을 보며 왜 여기서는 화살표함수를 썼지? 하면서 의아해하며 이 글을 정리했는데, 다시 한번 내가 헷갈렸던 내용들을 여기에 작성하려고 한다. 오직 내가 그냥 헷갈렸던 것이므로.. 그냥 말그대로 메모!
<script>
const scriptURL = 'https://script.google.com/macros/s/AKfycbzslEnJtPfNT6z0ohrXP9cZYGhWvKVsFjQV7eLcriT3tok5D5ty/exec'
const form = document.forms['submit-to-google-sheet']
form.addEventListener('submit', e => {
e.preventDefault()
fetch(scriptURL, { method: 'POST', body: new FormData(form)})
.then($("#form").trigger("reset"))
.catch(error => console.error('Error!', error.message))
})
</script>
화살표함수 쓰면 안된다고 했는데 왜 여기선 썼느냐?!!?
e를 사용해 이벤트 객체를 이용하는 것이므로(this 사용 안했잖니?)
출처
https://poiemaweb.com/es6-arrow-function
https://stackoverflow.com/questions/55327118/what-does-addeventlistener-e-mean
https://teamtreehouse.com/community/why-doesnt-my-arrow-function-work-in-my-addeventlistener