자바스크립트에서 함수는 매우 중요하다. 자바스크립트의 핵심 개념이라고 할 수 있는 스코프
실행 컨텍스트
클로저
생성자 함수에 의한 객체 생성
메서드
this
등등.. 함수와 모두 깊은 관련이 존재한다. 따라서 함수는 자바스크립트를 정확히 이해하고 사용하기 위하여 피해갈 수 없는 핵심 중의 핵심이다.
자바스크립트의 함수는 객체 타입의 값이다. 따라서, 함수를 함수 리터럴로 생성 가능하다.
여기서 리터럴이란, 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용하여 값을 생성하는 표기 방식이다.
var f = function add(x,y) {
return x + y;
}
위 코드는 변수에 함수 리터럴을 생성한 것이다.
함수 리터럴의 구성 요소는 다음과 같다.
이제 함수 리터럴이 어떻게 저장되는지 이해할 수 있다. 리터럴은 값을 생성하기 위한 표기법이므로, 함수 리터럴도 평가되어, 값을 생성하며 이 값은 객체라고 파악할 수 있다.
하지만 함수 객체가 가지는 큰 특징이 있는데..
일반 객체는 호출 불가능하지만, 함수는 호출할 수 있다.
일반 객체에는 없는 함수 객체만의 고유한 프로퍼티를 갖는다.
function add(x,y) {
return x + y;
}
var add = function(x,y) {
return x + y;
};
var add = new Function('x', 'y', 'return x + y');
var add = (x,y) => x + y;
여기서 함수 선언문
을 통하여 함수를 정의하는 방법에 대해서 재미있는 사실이 있다.
function add(x,y) {
return x + y;
}
//함수 선언문
함수 선언문은 함수 리터럴
과 형태가 동일합니다! 함수 리터럴
은
var add = function(x,y){
return x + y;
};
와 같이 함수 이름을 생략할 수 있으나, 함수 선언문
은 함수 이름을 생략할 수 없습니다!
그 이유는, 함수 선언문
은 표현식
이 아니라, 문
입니다..그래서 실행 완료하면 완료 값으로 당연히 undefined
가 출력된다. 함수 선언문
이 표현식인 문이라면 완료 값 undefined
대신에 표현식
이 평가되어, 생성된 함수가 출력될 것이다.
이 개념대로 간다면... 표현식
이 아닌 문
은 변수에 할당할 수 없을 텐데... 그럼 이건 뭐냐?
var add = function add(x,y) {
return x + y;
};
이 경우에는 자바스크립트 엔진 탓을 해야 하는데..
자바스크립트 엔진이 동일한 함수 리터럴을 코드의 문맥에 따라서 두 가지로 나누어 해석한다.
표현식
이 아닌 문
인 함수 선언문으로 해석하는 경우표현식
이 문
인 함수 리터럴 표현식으로 해석하는 경우갑자기 이게 뭔 소린가 싶고..억지 부리는 것 같이 보이지만! 결론적으로는 함수 선언문
은 함수 이름을 생략할 수 없다는 점을 제외하면, 함수 리터럴 표현식
과 동일하다. 그래서 함수 이름이 있는 기명 함수 리터럴은 함수 선언문
또는 함수 리터럴 표현식
으로 해석될 가능성이 있다는 것이다.
자바스크립트 엔진의 중의성 때문에 나타나는 문제이다. 이럴 경우 자바스크립트 엔진은 주변 코드의 문맥을 이용하여 상황을 해석하고, 해결해나간다.
위에 줄글로 풀어놓은 것을 일목요연하게 정리해보자면..
함수 선언문
으로 해석var add = function(x,y){
return x + y;
};
함수 리터럴 표현식
으로 석! function foo() {console.log('foo')}
//기명 함수 리터럴을 단독으로 사용하였기 때문에 함수 선언문으로 해석된다.
foo();
//함수 리터럴을 피연산자로 사용하면, 함수 선언문이 아니라 함수 리터럴 표현식으로 해석된다.
(function bar() {console.log('bar'); });
bar(); //bar is not defined 라면서 ReferenceError 발생
단독으로 사용된 함수 리터럴 foo
는 함수 선언문으로 해석된다.
그러나, 그룹 연산자인 ()
은 값으로 평가될 수 있는 표현식이 피연산자로 들어와야 하는데, 표현식이 아닌 문인 함수 선언문
은 피연산자로 사용할 수 없다. 즉 ()
안에 존재하는 함수 리터럴 bar
는 함수 선언문으로 해석되지 않고, 함수 리터럴 표현식
으로 해석된다.
이렇게 이름이 있는 함수 정의는 문맥에 따라서
함수 선언문
과함수 리터럴 표현식
두 가지로 해석될 수 있다.
자. 여기까지는 이해가 잘 되었다. 그런데 함수를 호출할 때, 왜 에러가 발생할까?
함수 이름은 함수 몸체 내에서만 참조할 수 있는 식별자이다.
이는, 함수 몸체 외부에서는 함수 이름으로 함수를 참조할 수 없다는 것을 의미한다.
foo
도 에러가 발생해야 하는 것 아닌가? 싶을 것이다..foo
라는 이름으로 함수를 호출하려면, 함수 이름이 아니라 함수 객체를 가리키는 식별자를 이용해야 한다. 그런데 나는 foo에 대한 식별자를 호출한 적이 없는데 참 이상하다...🙄foo는 자바스크립트 엔진이 암묵적으로 생성한 식별자이다.
자바스크립트 엔진은 암묵적으로 생성된 함수를 호출하기 위하여, 함수 선언문
을 해석하여 함수 이름과 동일한 이름의 식별자를 생성하고, 거기에 함수 객체를 할당
한다.
bar
같은 경우에는, 함수 객체를 가리키는 식별자가 따로 없는 것이다. 그래서 ReferenceError
가 발생하는 것이다.우리는 이번 공부를 통해서 사실 이거 하나만 얻어가면 된다... 자바스크립트 엔진은 함수 선언문
을 함수 표현식
으로 변환하여, 함수 객체를 생성한다. 그리고...
함수는 함수 이름으로 호출하는 것이 아니라, 함수 객체를 가리키는 식별자를 통하여 호출된다!
자바스크립트의 함수는 객체 타입의 값이므로 다음 행동들이 가능하다.
위에서 확인한 것과 같이, 함수 리터럴로 생성한 함수 객체를 변수에 저장할 수 있다. 이러한 방식을 함수 표현식
이라고 하였다.
var add = function(x, y){
return x + y;
};
이렇게 함수 리터럴의 이름은 생략이 가능하다. 어짜피 호출은 함수 이름이 아니라.. 함수 객체를 가리키는 식별자가 하는 것이니까!
var add = function foo(x,y){
return x + y;
};
console.log(add(2,5)) //정상 작동
console.log(foo(2,5)) //레퍼런스 에러 발생
위에서 언급한 애매한 말을 다시 정리할 필요가 있겠다.
표현식이 아닌 문
표현식인 문
자바스크립트가 기본적으로 제공하는 빌트인 함수인 Function
생성자 함수이다.
(여기서 생성자 함수는 객체를 생성하는 함수를 말한다.)
그런데 비추합니다. 😣 그 이유는 아래에..
ES6
에서 도입된 따끈따끈한 친구입니다. function
키워드 대신에 =>
를 사용하여 간략하게 함수를 선언한다. 이친구는 항상 익명 함수라는 점을 알아두세요..
const add = (x, y) => x + y;
console.log(add(2,5));
const arrow = (x,y) => {...};
매개변수가 한 개인 경우, 소괄호를 생략 가능하다.
const arrow = x = > {...};
매개변수가 없으면 소괄호를 생략할 수 없다.
const arrow = () => { ... };
const power = x => x ** 2;
다만 중괄호를 생략하였을 때, 함수 몸체의 내부의 문이 표현식이 아닌 문이라면 에러를 발생시킨다. 표현식이 아닌 문을 반환하면 어떡하겠니...
여러줄로 이루어지면 중괄호 생략하면 안돼~
사실 자바스크립트 공부를 깊게 하다 보면 기존 함수와 화살표 함수는 다른 점이 많다.
this
를 바인딩하는 방식이 다르다. (콜백 함수와 관련)