logo

DowanKim

43. 객체로서의 함수와 기명 함수 표현식

2026년 4월 15일

자바스크립트

자바스크립트에서 함수는 단순한 '코드 덩어리'가 아닙니다. 자바스크립트 세계의 함수는 **호출 가능한 객체(Callable Object)**입니다. 즉, 함수를 실행할 수 있을 뿐만 아니라 객체처럼 프로퍼티를 추가하고, 변수처럼 주고받을 수 있다는 뜻이죠.


🏗️ 1. 함수의 정체: "너, 사실 객체지?"

자바스크립트 엔진은 함수를 객체로 취급합니다. 덕분에 우리는 함수에 원하는 정보를 저장할 수 있습니다.

name 프로퍼티: 똑똑한 이름 찾기

함수 객체는 자신의 이름을 기억합니다. 익명 함수라도 할당된 변수의 이름을 통해 '문맥적 이름(contextual name)'을 알아내는 똑똑함을 보여줍니다.

  • 특징: sayHi.name처럼 접근 가능.
  • 예외: 적절한 이름을 추론할 수 없는 경우(예: 배열 내 익명 함수)엔 빈 문자열이 됩니다.

length 프로퍼티: 매개변수 개수 확인

함수가 선언될 때 정의된 매개변수의 개수를 알려줍니다.

  • 주의: **나머지 매개변수(...rest)**는 개수에 포함되지 않습니다.
  • 실무 활용: 인수의 개수에 따라 다르게 동작하는 **다형성(Polymorphism)**을 구현할 때 유용합니다.

🏷️ 2. 커스텀 프로퍼티 vs 클로저

함수에 직접 프로퍼티를 추가해 정보를 저장할 수 있습니다. (예: sayHi.counter = 0)

구분클로저 (Closure)함수 프로퍼티 (Property)
접근성외부에서 접근 불가능 (Private)외부에서 수정 가능 (Public)
용도상태 은닉 및 보안이 중요할 때외부에서 상태를 제어해야 할 때

⚠️ 주의: 함수 프로퍼티(f.count = 0)를 만든다고 해서 함수 내부에 지역 변수 let count가 생기는 것은 아닙니다. 둘은 완전히 별개의 데이터입니다.


🖋️ 3. 기명 함수 표현식 (Named Function Expression, NFE)

함수 표현식에 이름을 붙인 형태를 말합니다. 이 이름은 함수 내부에서만 유효합니다.

let sayHi = function func(who) { if (who) { alert(`Hello, ${who}`); } else { func("Guest"); // 내부 이름 func를 사용해 안전하게 재귀 호출! } };

🌟 왜 func이라는 이름을 따로 붙이나요?

외부에서 sayHi라는 변수명을 다른 값(예: null)으로 바꿔버려도, 함수 내부에서는 고유한 이름인 func을 통해 자신을 안전하게 호출할 수 있기 때문입니다. 이는 재귀 로직을 짤 때 코드의 견고함을 더해줍니다.


🎙️ 기술 면접 대비 (Interview Questions)

Q1. 함수의 length 프로퍼티와 arguments.length의 차이는 무엇인가요? (중급)

답변: **func.length**는 함수 선언 시 명시된 매개변수(Parameter)의 개수를 나타내며, 나머지 매개변수를 제외한 고정된 값입니다. 반면 **arguments.length**는 함수가 실행될 때 실제로 전달된 인수(Argument)의 개수를 나타내며 호출 시마다 달라질 수 있습니다.

Q2. 기명 함수 표현식(NFE)의 내부 이름은 언제 유용하게 쓰이나요? (심화)

답변: 함수가 선언된 변수 이름이 외부 코드에 의해 변경되거나 초기화될 가능성이 있을 때, 안전한 재귀 호출을 보장하기 위해 사용합니다. NFE의 내부 이름은 해당 함수 스코프 내에 고정되어 있으며 외부에서는 접근할 수 없으므로 이름 충돌 걱정 없이 자가 참조가 가능합니다.


임의의 수만큼 있는 괄호를 이용해 합계 구하기

중요도: 2

다음과 같이 작동하는 함수 sum을 만들어보세요.

sum(1)(2) == 3; // 1 + 2 sum(1)(2)(3) == 6; // 1 + 2 + 3 sum(5)(-1)(2) == 6 sum(6)(-1)(-2)(-3) == 0 sum(0)(1)(2)(3)(4)(5) == 15

힌트: 해당 함수 내부에서 쓸 수 있는 객체-원시형으로의 형 변환을 직접 구현해야 할 수도 있습니다.

테스트 코드가 담긴 샌드박스를 열어 정답을 작성해보세요.

해답

  1. sum은 함수를 반환해야만 이 모든 것이 의도한 대로 동작합니다.
  2. sum이 반환하는 함수는 호출될 때마다 현재 값을 메모리에 저장하고 있어야 합니다.
  3. 함수는 == 를 만났을 때 숫자가 되어야 합니다. 함수는 객체이므로 객체를 원시형으로 변환하기 챕터에서 설명한 것처럼, 객체-원시형으로의 형 변환이 일어날 텐데, 이때 메서드를 직접 구현해 원하는 대로 객체-원시형으로의 형 변환이 일어나게 할 수 있습니다.

답안은 아래와 같습니다.

function sum(a) { let currentSum = a; function f(b) { currentSum += b; return f; } f.toString = function() { return currentSum; }; return f; } alert( sum(1)(2) ); // 3 alert( sum(5)(-1)(2) ); // 6 alert( sum(6)(-1)(-2)(-3) ); // 0 alert( sum(0)(1)(2)(3)(4)(5) ); // 15

함수 sum은 실제로 한 번만 동작한다는 사실에 주목하시기 바랍니다. 함수 sum은 함수 f를 반환합니다.

이어지는 호출에서 함수 f는 매개변수를 currentSum에 추가하고 자신을 반환합니다.

f의 마지막 줄에는 재귀가 없습니다.

재귀가 있었다면 아래와 같이 생겼을 겁니다.

function f(b) { currentSum += b; return f(); // <-- 재귀 호출 }

위 함수 f는 호출 없이 자기 자신을 그대로 반환합니다.

function f(b) { currentSum += b; return f; // <-- 자신을 호출하지 않고 반환만 합니다. }

이렇게 자기 자신을 호출하지 않고 반환만 하면 다음 호출에서 함수 f를 사용할 수 있고, 자기 자신을 또다시 반환해 원하는 만큼 이 과정을 반복할 수 있습니다. toString 은 currentSum 을 반환해주므로 반환된 함수(객체)를 숫자 혹은 문자열로도 사용할 수 있죠. Symbol.toPrimitive나 valueOf를 사용해 객체를 숫자나 문자열로 변환할 수도 있습니다.

`function sum(a) {

let currentSum = a;

function f(b) { currentSum += b; return f; }

f.toString = function() { return currentSum; };

return f; }`


💡 Tech Lead의 한 줄 인사이트

사실 lodashjQuery 같은 라이브러리들이 $_라는 주요 함수에 수많은 메서드를 붙여서 제공하는 방식이 바로 이 '객체로서의 함수' 특징을 극한으로 활용한 사례입니다. Egemoney의 유틸리티 함수나 SIMVEX의 수학 계산 모듈을 설계할 때, 함수 하나에 관련 헬퍼들을 프로퍼티로 묶어두면 전역 오염을 막으면서도 아주 깔끔한 API를 만들 수 있습니다.

함수가 단순한 실행 단위가 아니라 '상태를 가질 수 있는 객체'라는 관점, 이제 조금 익숙해지셨나요? 혹시 sum(1)(2)(3) 같은 연쇄 호출 로직을 실제 프로젝트의 필터링 시스템 등에 응용해보고 싶은 생각이 드시는지 궁금합니다! 다음 주제인 **new Function**으로 넘어가 볼까요?