출처 모던 자바스크립트 Deep Dive을 보고 정리한 내용입니다.
함수란 일련의 과정을 문으로 구현하고 코드 블럭으로 감싸 하나의 실행단위로 정의한 것을 말한다.
//함수 정의
function add(num1, num2) { //함수 이름을 명명한다. 매개변수로 입력값을 받는다.
return num1 + num2; //반환값
}
//함수 호출
add(1, 2); //함수 식별자에 인수를 넣어 호출
자바스크립트에서 함수는 객체 타입이다. 따라서 함수도 리터럴로 생성할 수 있다.
함수를 선언문으로 사용할 때 함수의 이름은 생략할 수 없으나 함수 표현식으로 사용된 경우 함수의 이름을 생략할 수 있다. 또한 함수 이름은 함수 몸체 내에서만 참조할 수 있는 식별자이다.
함수 정의 방식은 다음과 같다.
함수 선언문
function add(num1, num2){
return num1 + num2;
}
함수 표현식
var add = function (num1, num2) { //기명 함수
return num1 + num2;
}
var add = function add (num1, num2) { //무명 함수 or 익명 함수
return num1 + num2;
}
Function 생성자 함수
var add = new Function('num1', 'num2', 'return num1 + num2');
화살표 함수(ES6)
var add = (num1, num2) => {
return num1 + num2;
}
함수 선언문은 표현식이 아닌 문이다. 그러나 함수 표현식에서 변수에 함수의 선언문이 할당되는 것처럼 보인다. 자바스크립트 엔진은 문맥을 통해 함수가 선언문 또는 표현식으로 쓰이는지를 해석한다. 함수선언 또는 함수 표현식에서 사용되는 중괄호 {} 는 블록문으로도 사용되고, 객체 리터럴로도 사용되는 중의적인 표현이다. {}가 단독으로 사용되면 자바스크립트 엔진은 이를 블록문으로 해석하여 함수 선언문으로 인식하고 동작한다. 할당 연산자의 우변과 같이 피연산자로서 사용되는 경우 {}를 객체 리터럴로 인식하여 표현식으로써 동작한다.
function foo() {console.log('foo');}
foo(); //'foo'
(function foo2() {console.log('foo2');});
foo2(); //RefernceError
foo함수는 호출이 가능하고, foo2 함수는 참조에러가 발생한다. 그 이유는 무엇일까?
foo함수는 {}가 단독으로 사용되어 함수 선언문으로써 해석되어 호출이 가능한 것을 알 수 있다. 그러나 foo2는 그룹연산자 () 내에 있기 때문에 자바스크립트 엔진은 이를 표현식으로 해석하였다. 함수 리터럴의 '함수 이름은 함수 몸체 내에서만 참조할 수 있는 식별자이다'라는 특징을 기억하고 있을 것이다. 그러므로 함수의 이름인 foo2는 함수 내부에서만 사용이 가능하고, 외부에서 사용가능한 식별자는 정의되지 않았기 때문에 호출할 수 없는 것이다.
foo함수는 어떻게 foo라는 이름으로 호출할 수 있었을까? 함수 선언문으로 함수를 선언하면 자바스크립트 엔진은 암묵적으로 함수 이름으로 식별자를 생성한 후 함수 객체를 할당한다. 즉, 함수를 선언하면 아래와 같은 코드로 동작한다.
var foo = function foo() {
console.log('hello');
}
자바스크립트의 함수는 변수에 할당이 가능하고, 프로퍼티의 값이 되거나 배열의 요소가 될 수 있다. 값의 성질을 갖는 객체를 일급객체라고 하며 자바스크립트의 함수는 일급객체이다. 그렇기 때문에 표현식으로 변수에 함수 객체가 할당가능하다.
var add = function(num1, num2) {
console.log(num1 + num2);
}
add(1, 2); //3
앞서 서술하였듯 함수리터럴에서 함수의 이름은 함수 몸체 내에서만 참조할 수 있는 식별자이기 때문에 다음과 같은 코드는 오류를 발생시킨다.
var add = function foo(num1, num2) {
console.log(num1 + num2);
}
foo(1, 2); //TypeError
foo라는 이름을 가진 함수 객체가 값으로써 저장되어 있고, foo 함수 객체 값이 저장된 공간 주소를 add라는 식별자로 가리키고 있다. 그렇기 때문에 foo라는 이름으로 직접 함수에 접근 할 수 없다.
Function 생성자 함수에 매개변수 목록과 몸체를 전달하면 함수 객체를 생성하여 반환한다.
var add = new Function('num1', 'num2', 'return num1 + num2');
console.log(add(1, 2)); //3
Function 생성자 함수로 정의한 함수는 클로저(closure)를 갖지 않는 등, 함수 선언문이나 표현식으로 생성한 함수와 다르게 동작하기 때문에 주위를 요해야한다.
var add1 = (function() {
var a = 1;
return function(num1, num2) {
return num1 + num2 + a;
}
}());
console.log(add1(2, 3));
var add2 = (function() {
var a = 1;
return new Function('num1', 'num2', 'return num1 + num2 + a');
}());
console.log(add2(2, 3)); //ReferenceError
기존 함수보다 표현이 간결해진 것 뿐만 아니라 동작 또한 간소화 되었다. 기존 함수와 this 바인딩 방식, prototype 프로퍼티가 없으며 arguments 객체를 사용하지 않는다.
const add = (num1, num2) => num1 + num2;
console.log(add(1, 2)); //3