안녕하세요. 김용성입니다.
오늘은 JavaScript의 함수 선언 방식과 호이스팅, 그리고 Arrow function과 일반 함수가 어떻게 다른지에 대해 간단히 설명드리고자 합니다.
//함수 선언문
function myFunc(){
console.log("yongseong.log")
}
myFunc()
위 코드를 살펴볼까요? 그냥 간단한 함수 생성이죠???
여러분들은 아마 이런 코드를 수도 없이 작성해보았을겁니다.
위 코드를 조금 변경해보겠습니다.
//함수 표현식
let myFunc= function(){
console.log("yongseong.log")
}
myFunc()
이런 코드도 많이 접해보셨죠?? 둘은 무슨 차이점을 가지고 있을까요?
자 먼저 명칭에 대해 말씀드리면 첫번째 코드는 함수 선언문이라고 부르고 두번째 코드는 함수 표현식이라고 부른답니다.
✋ 둘의 결과 값이 같게 나오는데 혹시 어떤 차이점이 존재하나요???
맞습니다. 둘다 console에 "yongseong.log"라는 내용을 보이게 할 것이고 딱히 차이점을 느끼시지 못할겁니다. 그러나 myFunc()라는 호출문을 한번 각각 함수 선언문과 함수 표현식의 상단부분으로 이동시킨 뒤 코드를 실행시켜보면 어떨까요? 이런식으로 말이죠.
//함수 선언문
myFunc()
function myFunc(){
console.log("yongseong.log")
}
//함수 표현식
myFunc()
let myFunc= function(){
console.log("yongseong.log")
}
함수 선언문 으로 작성된 함수는 호출이 잘 됩니다. 그렇지만 함수 표현식 작성된 함수는 에러가 발생할 것이예요😂.
✋ JavaScript 코드는 위에서부터 아래로 차례대로 실행된다고 알고 있는데 첫번째 코드가 문제없이 실행되는 것이 신기해요..
그것은 바로 JavaScript 내부의 알고리즘으로 인한 현상입니다.
JavaScript는 코드의 실행 전 코드 내부의 모든 함수 선언문 들을 모두 다 파악해놓습니다.
따라서 함수 선언문으로 작성된 함수들은 순서에 상관 없이 위치와 관계없이 호출이 가능한 것이죠.
함수 선언문으로 선언된 함수들의 사용 가능 범위가 해당 코드의 위치보다 위로 올라갈 수 있는 것을 바로 호이스팅이라고 합니다.
반면에 함수 표현식으로 생성된 함수는 차례대로 읽히는 함수이므로 선언된 위치보다 위에서 호출이 될 경우 에러가 발생하게 되는 것이죠.
개인적으로 저는 함수 선언문을 주로 사용하는 것이 자유롭고 편하긴 하지만, 협업을 할 때에는 함수 표현식을 사용하여 human error에 대해 미리 방지하는 것도 좋다고 생각합니다.
이해되셨나요?
let myFunc= () => {
console.log("yongseong.log")
}
myFunc()
화살표 함수로 선언된 방식
이제 ES6에서 추가된 Arrow function에 대해 알아보도록 하겠습니다.
Arrow function과 일반 function의 차이를 잘 모르고 편한대로 남발하던 때가 있었습니다.
왜냐, 둘이 어떤 차이점이 있는지 잘 알지 못했기 때문이죠..
여러분들은 오늘 제 포스팅을 읽고 Arrow function과 일반 function의 차이점에 대해 꼭 알아가시면 좋겠습니다..!
면접 질문에서도 이를 물어보는 경우가 종종 있어요 ㅎㅅㅎ
Arrow function은 혜성처럼 등장해서 일반 function에 비해 구문이 짧고 간편하다는 이유로 우리의 JavaScript 코딩에서 많이 쓰이는 녀석입니다.
그렇지만 Arrow function과 일반 function과 단순히 문법만 다른 것이 아닙니다.
일반 function에겐 있지만 Arrow function에게는 없는 것이 딱 3가지 있습니다. 지금부터 설명드리겠습니다.
Arrow function은 아주 불쌍한 녀석이라 이름이 없어요. 익명 함수이기 때문에 handling을 위해선 반드시 앞에 변수 선언문을 사용하여 변수에 할당해주어야 하죠. Arrow function은 익명 함수라는 점 꼭 알아두세요 ㅎㅎ
//일반 function
function haveName(){
console.log("나는 이름이 있어!")
}
//arrow function
() => { //-> const haveName=()=>{
console.log("나는 이름이 없어..")
}
말로 설명하기에 조금 복잡하기때문에 바로 예제 코드를 보여드리도록 하겠습니다.
// this example
let myInfo={
name:"yongseong",
show: function(){
console.log(this.name);
}
};
myInfo.show();
this는 호출하는 방법에 의해 결정되며, 위 코드에서는 myInfo.show()라는 호출문이 사용되었기때문에 myInfo가 this의 주체가 됩니다.
이번에는 show 내부에 어떤 콜백함수를 만들어 준다고 생각해보겠습니다.
'yongseongBtn'이라는 Id를 가진 button element를 가져와서 click event로 myInfo.name을 호출하고자 합니다.
// binding scopes
const btn=document.getElementById('yongseongBtn');
let myInfo={
name:"yongseong",
show: function(){
console.log(this.name);
btn.addEventListener('click',(function(){
console.log(this);
}).bind(this));
}
};
myInfo.show();
해당 코드는 잘동작합니다.
이처럼 우리는 bind(this) 코드를 사용해서 btn을 통해 호출된 함수일지라도 해당 this 값을 상위 스코프 this와 묶어주며 해당 과정을 수행할 수가 있죠??
근데 arrow Function에는 this가 존재하지 않아요. 즉 binding을 해 줄 필요가 없다는 것이죠. show 내부의 콜백함수를 arrow function으로 변경해볼까요?
// arrow callback function
const btn=document.getElementById('yongseongBtn');
let myInfo={
name:"yongseong",
show: function(){
console.log(this.name);
btn.addEventListener('click',(()=>{
console.log(this);
});
}
};
myInfo.show();
어떤가요?
bind를 사용하지 않았는데도 console에 "yongseong"이라고 잘 보여지는 것을 확인하실 수 있을겁니다. arrow function에 this라는 것이 존재하지 않기에 상위 function으로 스코핑이 넘어가고, 그 함수가 선언된 시점으로 스코핑이 넘어가서 myInfo를 this로 받게되기에 이러한 현상이 나타나는 것입니다.
이해되셨나요??
Arrow function은 arguements가 존재하지 않아요.
한번 살펴볼까요?
// 일반 function
const myFunc=function(){
console.log(arguments);
}
myFunc(1,2,3,4);
이렇게 일반 function에서는 arguments라는 키워드를 통해 호출 시 입력된 매개변수를 활용할 수가 있었는데요.
Arrow function을 통해 이를 만들어볼까요?
// Arrow function
const myFunc2 = () => {
console.log(arguments);
}
myFunc2(1,2,3,4);
위 코드를 실행시켜보면
이러한 에러가 발생한다는 것을 보실 수 있을겁니다.
좀 더 확실하게 증명하기 위해서 myFunc2라는 arrow function을 일반 function으로 한번 더 감싸주겠습니다.
// Arrow function in 일반 function
function outer(){
const myFunc2 = () => {
console.log(arguments);
}
myFunc2();
}
outer(1,2,3,4)
위 코드를 실행시켜보시면 일반 function의 실행결과와 똑같은 결과가 출력됨을 보실 수 있을겁니다.
내부 myFunc2에는 arguments라는 것이 존재하지 않으므로 스코프 체이닝을 통해 외부 outer의 arguments들을 인자로 받는 것이죠.
그래서 Arrow function의 경우 arguments를 추가시켜주기 위해서는 arguments를 직접 명시해주어야 합니다! 이럴 때는 ES6에서 같이 추가된 전개연산자 등을 활용하면 좋아요.
// 전개연산자 사용
const myFunc2 = (...arguments) => {
console.log(arguments);
}
myFunc2(1,2,3,4);
전개 연산자를 사용할 경우에는 arguments라는 요소 자체를 배열로 받아주기 때문에 사용하는 것도 굉장히 간단하죠. 알아두시면 도움되실거라 생각합니다!
오늘 글 이해되셨나요?
제가 처음 JavaScript를 접했을 때 가장 헷갈렸던 것이 Arrow function과 일반 function이었던 것 같아요. 정말 기본적인 것이지만 면접에서 많이 물어보는 것 중 하나이기도 하죠. 개인적으로 이 사람이 개발을 할 때 해당 언어에 대해 얼마나 파악하고 진행하는 사람인가에 대해 파악하기 가장 적절한 질문이라고 생각합니다.
최대한 쉽게 풀어쓰고자 노력을 했는데 여러분들에게 많은 도움이 되면 좋겠습니다.
포스팅 글 읽어주셔서 감사합니다.