1. 함수 선언식(function declaration)

1
2
3
4
function func_name(parameter1, parameter2)
{
    ...
}

한편, 다음과 같이 함수를 앞뒤로 ()로 감싸고 그 뒤에 ();를 덧붙이면 마치 그 지점에서 함수를 호출한 것과 같이 그 함수 내용이 실행된다.

1
2
3
4
(function func_name(parameter1, parameter2)
{
    ...
})(argument1, argument2);

2. 익명함수(anonymous function)와 함수 표현식(function expression)

- 자바스크립트에서는 함수명을 쓰지 않고 함수를 선언해도 맞는 문법으로 간주한다. 예를 들면, 다음과 같이 쓰더라도 문법 오류가 발생하지 않는다.

1
2
3
4
function()
{
   ...
}

- 이런 식으로 코드를 쓰면 함수를 호출을 할 방법이 없으므로 이대로는 이 함수의 내용이 실행되는 일은 없다. 이와 같은 익명함수는 다른 함수의 인자로 함수를 대입해 전달하고자 할 때 흔히 쓰인다. (참고로 이처럼 다른 함수의 인자로 넘겨지는 함수가 그 다른 함수의 수행 과정에서 호출이 일어나게 될 때 이 인자로 넘겨진 함수를 callback 함수라 한다.)

- 한편 다음과 같은 방법으로 익명함수를 변수에 대입할 수 있으며 (이를 함수 표현식이라 한다), 이후 그 변수명을 호출하여 마치 함수명이 있는 함수를 선언한 것처럼 그 함수 내용을 실행시킬 수도 있다.

1
2
3
4
5
6
var func_name = function(parameter1, parameter2) {
    ...
}


func_name(argument1, argument2); //이처럼 함수를 호출하여 func_name에 저장된 익명함수를 호출할 수 있다.

- 자바스크립트의 경우 코드가 인터프리터에 의해 수행될 때 인터프리터가 변수와 함수의 선언 내용을 가장 먼저 메모리에 수집한다_(이를 영어로 ‘끌어올리다’라는 뜻을 가진 hoisting이라 표현하며, 흔히 ‘선언부가 코드의 상단으로 옮겨진다’라고 표현한다). 따라서 함수 선언식으로 선언된 함수의 경우 함수 선언식이 뒤쪽에 있고 그 앞에서 함수를 호출하는 코드를 썼다고 하더라도 코드가 정상적으로 작동한다. 반면 함수표현식으로 함수를 선언한 경우 변수 선언부는 다른 선언부와 마찬가지로 인터프리터에 의해 가장 먼저 수집되지만 함수표현식으로 표현된 함수는 이와 함께 수집되지 않는다. 이렇게 되면 함수 호출 시 ‘선언만 되고 값은 지정되지 않은 함수 아닌 변수’를 호출하는 셈이 되며, 따라서 이 경우 함수 선언식으로 선언한 코드와 달리 에러(함수로 정의되지 않은 변수를 함수를 호출하는 형식으로 호출)_가 발생한다.

- 이와 같은 상황에서 에러를 발생시키는 함수 표현식과 달리 에러를 발생시키지 않는 함수 선언식의 특성 때문에, 함수 선언식을 사용하는 경우 도중에 코딩을 잘못 하게 되더라도 디버깅을 하기 어려운 경우가 많다. 꼭 필요한 상황이 아니면 함수 표현식으로 함수를 사용하는 편이 낫다.

3. 람다식

- 자바스크립트에서 function 예약어를 쓰는 대신 (), =>, {}를 연달아 쓰는 것만으로 익명함수를 선언할 수 있으며 이를 화살표 함수(arrow function) 또는 람다식(lambda expression)이라 한다. 다음은 람다식으로 선언한 익명함수의 한 예이다.

1
2
3
(parameter1, parameter2) => {
   ...
};

- function 예약어를 쓴 익명함수와 마찬가지로, 이런 식으로 코드를 쓰면 함수를 호출을 할 방법이 없으므로 이대로는 이 함수의 내용이 실행되는 일은 없다.

- function 익명함수와 마찬가지로, 람다식을 변수에 대입하여 변수명을 호출함으로써 람다식 내용을 실행시킬 수 있다.

1
2
3
4
5
6
const func_name = (parameter1, parameter2) =>
{
    ...
}

func_name(argument1, argument2);

- 한편, 람다식에서 => 앞의 괄호와 => 뒤의 중괄호는 반드시 써야 하는 것은 아니며 괄호는 파라미터가 하나일 때, 중괄호는 안의 내용이 코드 한 줄일 때에는 생략할 수도 있다. (즉, 얼핏 봐서는 전혀 함수가 없는 것처럼 보이는 코드라 해도 실은 =>가 쓰인 곳에는 모두 함수가 사용되고 있다고 봐야 한다.) 예를 들어 다음과 같은 코드를 쓸 수 있다.

1
console.log(( _ => "농장")("사과"));

- 이처럼 중괄호를 생략하는 경우에는, => 오른쪽에 있는 식이 이 람다식의 리턴값으로서 리턴된다. 따라서 위 코드의 경우 다음과 같이 출력된다.

농장

4. 람다식 사용 시 주의사항(this 객체 관련)

- 함수를 쓰다 보면, 함수 내부에서 this 객체를 사용하는 경우가 종종 있다. 이처럼 함수 내부에서 사용하는 this 객체는 해당 함수가 특정 객체의 멤버 함수로서 호출되었을 때 그 객체를 가리키기 위해 사용된다. 다음은 함수 표현식으로 선언한 함수를 객체의 멤버 함수로 지정해 호출하는 예제이다.

1
2
3
4
5
6
7
8
const getValOfP1_func = function () 
{
    return this.key1;
}

const obj = { key1: '값1', getValOfP1: getValOfP1_func };

console.log(obj.getValOfP1());

- 람다식의 경우 그 내부에서 this 객체를 사용하면, 그 람다식의 이름을 위와 같은 방식으로 객체의 메서드로서 호출한다 하더라도 일반적인 방식으로 함수를 선언한 경우와 달리 this 객체를 자기 자신을 호출한 객체로 받아들이지 않고 전역변수로 인식하는 경우가 있다. (자바스크립트 인터프리터는 코드에서 변수/함수 선언 부분을 다른 코드보다 먼저 메모리에 수집하는데, 람다식 내부에 있는 this 객체의 메모리 확보 또한 이러한 메모리 수집 작업이 일어날 때 함께 일어난다. 따라서 선언부 뒤에 있는 코드가 실행될 때 람다식을 호출하면 람다식 내부의 this 객체 부분이 이미 전역변수로서 메모리에 확보돼 있게 되고, 이로 인해 객체의 메서드로서 람다식을 호출한다 하더라도 람다식 내부의 this 객체는 그 람다식을 호출한 객체가 아닌 전역변수를 가리키게 되는 것이다.)

1
2
3
4
5
6
7
8
var key1 = "abc";
const getValOfP1_lambda = () => {
    return this.key1;
}

const obj = {key1: "def", getValOfP1: getValOfP1_lambda};

console.log(obj.getValOfP1());

- 위 코드에서는 getValOfP1_lambda가 obj의 메서드로서 호출되나, 출력값으로 obj의 멤버 key1이 갖는 값인 “def”가 아니라 전역변수 key1의 “abc”가 출력된다.