함수

함수란?

자바스크립트에서 가장 중요한 핵심 개념으로, 다른 핵심 개념인 스코프, 실행 컨텍스트, 클로저, this, 프로토타입 등이 모두 함수와 깊은 관련이 있다.

함수는 일련의 과정을 문(statement)로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것이다.

함수는 왜 사용하는가?

함수는 한번 정의를 해놓으면 사용자가 필요할 때마다 계속해서 호출할 수 있다. 실행 시점을 개발자가 결정하고, 몇 번이든 재사용이 가능하다.
따라서 함수는 코드의 재사용 이라는 측면에서 매우 유용하다!

함수가 없다면?

만약 함수가 없다면 같은 로직을 요하는 결과가 여러번 나올때마다 똑같은 코드를 계속 작성해 줘야한다.

// q1. 1 + 2 =?
x = 1;
y = 2;
result = x + y;

// q2. 2 + 3 =?
x = 2;
y = 3;
result = x + y; 

// q3. 4 + 5 =?
x = 4;
y = 5;
result = 4 + 5;

...

// 함수를 이용할 경우 한번만 작성 후 인수를 넣어 호출만 해주면 된다.
function add(x, y) {
	return x + y;
}
// q1. 1 + 2 =?
result = add(1, 2);
// q2. 2 + 3 =?
result = add(2, 3);
// q3. 4 + 5 =?
result = add(4, 5);

함수 리터럴

함수는 객체 타입의 이다. 따라서 숫자 값을 리터럴로 생성하고, 객체를 리터럴로 생성하는 것처럼 함수도 함수 리터럴로 생성할 수 있다.
함수 리터럴은 function 키워드, 함수 이름(생략 가능), 매개 변수 목록, 함수 몸체로 구성된다.

var func1 = function sum(x, y) {
 return x + y;
}

var func2 = function (x, y) {
 return x + y;
} // 이름 생략 가능

함수 정의 방법

자바스크립트에는 함수를 정의하는 4가지 방법이 존재한다.

함수 선언문 (function declaration)

function add(x, y) { 
	return x + y; 
}

함수 선언문은 표현식이 아닌 문(statement)이다.

표현식이 아닌 문은 변수에 할당할 수 없다고 배웠다. 그러나 아래의 코드는 함수 선언문이 변수에 할당되는 것처럼 보인다.

var add = function add(x, y) {
	return x + y;
};

이렇게 동작하는 이유는 자바스크립트 엔진이 코드의 문맥에 따라 함수 리터럴을 함수 선언문으로 해석하는 경우와 함수 선언문을 함수 리터럴 표현식으로 해석하는 경우가 있기 때문이다.

예를 들어, 중괄호 { }은 블록문일 수도 있고, 객체 리터럴일 수도 있다. { }처럼 중의적인 코드는 문맥에 따라 해석이 달라진다.
자바스크립트 엔진은 { } 이 단독으로 존재하면 블록문으로 해석하고, 값으로 평가되야 할 문맥에서 피연산자등으로 사용되면 객체 리터럴로 해석한다.

function foo() { console.log('foo'); }
foo(); // foo

(function bar() { console.log('bar'); });
bar(); // ReferencError

위 예제에서 단독으로 사용된 foo 는 함수 선언문으로 해석되고, 그룹 연산자 ( ) 내에 있는 함수 선언문은 함수 리터럴 표현식으로 해석된다. 그룹 연산자의 피연산자는 값으로 평가될 수 있는 표현식이어야 하기 때문이다.

둘 다 함수 객체를 생성한다는 점에서는 동일하지만 호출에서 차이가 있다.
foo 는 호출이 가능하지만 bar 는 가능하지 않다.
함수 이름은 함수 몸체 내에서만 참조할 수 있는 식별자다. 이는 함수 몸체 외부에서는 함수 이름으로 함수를 참조할 수 없으므로 외부에서는 호출할 수 없다는 뜻이다.

하지만 foo 도 함수 내부에서만 유효한 식별자이므로 foo 도 호출할 수 없어야한다. foo 라는 이름으로 호출하려면 foo 는 함수 이름이 아니라 함수 객체를 가리키는 식별자여야 한다. 하지만 위 예제에서는 foo 를 선언한적도, 할당한적도 없다.

결론부터 말하자면 foo 는 자바스크립트 엔진이 암묵적으로 생성한 식별자이다.

자바스크립트 엔진은 생성된 함수를 호출하기 위해 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고, 거기에 함수 객체를 할당한다.

함수는 함수 이름으로 호출하는 것이 아니라 함수 객체를 가리키는 식별자로 호출한다.

즉, 함수 선언문으로 생성한 함수를 호출하는 것은 함수 이름 add가 아니라 자바스크립트 엔진이 암묵적으로 생성한 식별자 add인 것이다.

이는 아래의 코드를 보면 확실하게 알 수 있다.

let add;  

function add() {  
} // SyntaxError: Identifier 'add' has already been declared

위에서 이미 add라는 변수를 생성했고, 밑에서 함수 add를 만들려고 했다. 그러나 자바스크립트가 암묵적으로 add라는 식별자를 생성하기 때문에 함수 선언문 라인에서 에러가 발생한다.

함수 표현식

자바스크립트의 함수는 객체이면서 값처럼 변수에 할당할 수 있고, 프로퍼티 값이 될 수도 있으며, 배열의 요소가 될 수도 있다. 이러한 값의 성질을 갖는 객체를 일급 객체 라고 한다.

함수는 일급 객체이므로 함수 리터럴로 생성한 함수 객체를 변수에 할당할 수 있다. 이러한 함수 정의 방식을 함수 표현식이라 한다.

// 익명 함수 표현식
var add = function(x, y) {
	return x + y;
};
console.log(add(2, 5)); // 7

// 기명 함수 표현식
var add = function foo(x, y) {
	return x + y;
};

console.log(foo(2, 5)); // ?

함수는 함수 이름이 아니라 식별자를 통해 호출한다.

Function 생성자 함수

자바스크립트가 기본 제공하는 빌트인 함수다.
Function 생성자 함수에 매개변수 목록과 함수 몸체를 문자열로 전달하면서 new 연산자와 함께 호출하면 함수 객체를 생성하여 반환한다. (new 연산자 없이 호출해도 결과는 동일하다.)

var add = new Function('x', 'y', 'return x + y');

Function 생성자 함수로 함수를 생성하는 방식은 일반적이지 않으며 바람직하지도 않다.

Function 생성자 함수로 생성한 함수는 클로저를 생성하지 않는 등. 함수 선언문이나 표현식으로 생성한 함수와 다르게 동작한다.

화살표 함수

ES6에서 도입된 화살표 함수는 Function 키워드 대신 화살표 => 를 사용해 좀 더 간략한 방법으로 선언할 수 있다.
화살표 함수는 항상 익명 함수로 정의한다.

const add = (x, y) => x + y;

화살표 함수는 표현만 간략한 것이 아니라 내부 동작 또한 간략화되어 있다.


참조