MDN Web Docs에서 자바스크립트 언어에 대한 소개를 보면 첫 문장은 다음과 같다.
JavaScript (JS)는 가벼운, 인터프리터 혹은 just-in-time 컴파일 프로그래밍 언어로, 일급 함수를 지원합니다.
우선 인터프리터, just-in-time 컴파일 언어는 나중에 살펴보기로 하고, 이번 포스트에서는 이 '일급 함수'라는 개념에 집중하고자 한다. 자바스크립트는 일급 함수를 지원하는 언어인데, 이게 무슨 말일까? 일급 함수란 무엇을 뜻하는 용어일까?
일급 함수란?
먼저, MDN Web Docs에서 일급 함수에 대한 설명을 참고하면 다음과 같다.
프로그래밍 언어는 해당 언어의 함수들이 다른 변수처럼 다루어질 때 일급 함수를 가진다고 합니다. 예를 들어, 일급 함수를 가진 언어에서 함수는 다른 함수들에 전달인자로 제공되고, 다른 함수에 의해 반환될 수 있으며, 변수에 값으로서 할당될 수 있습니다.
요약하면 어떤 언어에서 함수를 '값'처럼 다룰 수 있을 때 이 언어를 '일급 함수를 가진다'고 표현한다. 그렇기에 함수가 '값'처럼 다른 함수들에 전달인자로 넘겨질 수 있는 것이고, 다른 함수에 의해 '값'처럼 반환 될 수 있으며, 변수에 '값'으로서 할당될 수 있다는 것이다.
아래 코드는 자바스크립트에서 함수가 값처럼 다뤄지는 간단한 예시이다.
const sayHello() = function(name) {
return `Hello ${name}!`;
};
console.log(sayHello());
함수를 값으로 취급하는 것이 가능한 이유
그렇다면 함수가 이렇게 값처럼 취급을 받을 수 있는 이유는 무엇일까? 그것은 바로 함수가 자바스크립트에서 실제로는 '객체'이기 때문이다. 모든 함수는 Function이라는 객체의 인스턴스이며, 다른 객체와 동일한 방식으로 동작한다.
Function이라는 객체는 내부에 call()이라는 메소드를 가지고 있다. 이 인스턴스는 함수가 호출될 때 실행되는 메소드로, 함수가 객체임에도 호출이 가능하도록 해준다.
그리고 함수는 객체이기에 아래 코드처럼 속성(property)를 추가해줄 수도 있다.
function greet(name) {
return `Hello, ${name}!`;
}
greet.language = 'English';
console.log(greet.language); // 출력: English
또 함수는 동적으로 생성할 수 있다. 아래 코드처럼 Function 생성자를 사용해주면 된다.
const add = new Function('a', 'b', 'return a + b;');
console.log(add(2, 3)); // 출력: 5
부가 설명을 덧붙이면, 자바스크립트의 함수는 자신이 정의된 스코프(scope)를 기억하는 클로저(Closure) 특성을 가진다. 이는 함수가 생성될 때 그 주변 환경을 함께 저장하여, 이후에 해당 환경에 접근할 수 있게 해주는 역할을 수행한다. 자세한 내용은 클로저에 관한 포스트에서 따로 다뤄보도록 하겠다.
더 알아보기: 함수 객체의 프로퍼티
함수는 객체이므로 프로퍼티를 가진다. 함수 객체는 일반 객체에 존재하지 않는 고유 프로퍼티를 가지고 있다고 한다. 크롬의 개발자 도구를 이용하여 한 번 함수의 프로퍼티 종류를 알아보자.
보시다시피 함수 객체에는 arguments, caller, length, name, prototype이라는 프로퍼티를 가지고 있다. 정확히 말하면 인스턴스 속성으로 이는 인스턴스 메서드와는 다르다. (caller은 인스턴스 속성이고 위에서 언급된 call()은 인스턴스 메서드이다)
자세한 내용은 여기를 확인해보면 알 수 있다.
arguments 프로퍼티
각각의 함수 객체 프로퍼티에 대해 짧게 설명해보고자 한다. 먼저 arguments 프로퍼티의 값은 객체이며, 이 arguments 객체는 함수에서 매개변수로 전달된 인수들을 담는 유사 배열이다. 정확히 말하면 유사 배열 객체이자 이터러블인데, (for문을 통해) 순환이 가능한 객체이지만 배열은 아닌 녀석이라고 생각해주면 될 듯 싶다.
자바스크립트에서는 매개변수의 개수만큼 인수를 전달하지 않아도 오류가 발생하지 않는다. 함수가 호출되면 매개변수는 undefined로 초기화되고 인수들의 값으로 할당받는데, 만일 인수의 개수가 매개변수 개수보다 적다면, undefined로 초기화된 매개변수가 그대로 존재할 것이고, 인수의 개수가 매개변수보다 많다면, 일부 인수는 매개변수에 담기지 않게 된다. 그래도 이 인자 정보를 담고 있는 argument 객체를 통해 전달된 모든 인수를 확인할 수 있다. 즉, 자바스크립트는 argument 객체 덕분에 가변 인자 함수를 다루기 유용한 셈이다.
// 방법 1: 유사 배열 arguments 활용
// 유사 배열: 자바스크립트의 함수가 호출될 때 함께 전달되는 배열
function sum2() {
let s = 0;
for (let i = 0; i < arguments.length(); i++) {
s += arguments[i];
}
return s;
}
const argSum = sum2(10, 20, 30, 40, 50); // 150
// 문제점: 함수의 시그니쳐를 통해 인자의 정보를 알 수 없다. 함수 사용에 불편함을 준다.
ES6에서는 Rest 파라미터를 지원하여 가변 인자를 더 쉽게 다룰 수 있도록 도와준다.
// 방법 2: 전개 파라미터(rest parameter) 활용
function sum3(...args) {
let s = 0;
for (let i = 0; i < args.length(); i++) {
s += args[i];
}
return s;
}
function sum4(a, b, ...args) {
let s = a + b;
for (let i = 0; i < args.length(); i++) {
s += args[i];
}
return s;
}
// (a, b, ...args) 매개변수 a, b에 값이 담기고 나머지 값이 순서대로 args 배열에 담긴다.
caller 프로퍼티
caller 프로퍼티는 ECMAScript 사양에 포함되지 않은 비표준 프로퍼티이다. 모던 자바스크립트 딥다이브 책에 따르면, 이후 표준화 될 예정도 없는 프로퍼티이므로, '함수 객체의 caller 프로퍼티는 함수 자신을 호출한 함수를 가리킨다' 이 정도만 참고로 알아두고 넘어가도 괜찮다고 한다.
length 프로퍼티
함수 객체의 length 프로퍼티는 함수의 매개변수 개수를 가리킨다. 이 때 주의할 점은 함수 객체의 length 프로퍼티는 인자의 개수를 가리키지 않는다는 것이다. 인자로 3개의 값이 들어오더라도 선언한 함수의 매개변수 개수가 2개라면, 함수 객체의 length 프로퍼티의 값은 2이다. 그렇다면 인자의 개수를 가리키는 값은 무엇이 있을까? 바로 아까 설명한 arguments 객체의 length 프로퍼티 값이 인자의 개수를 나타낸다.
function func(a, b, c) {
return a * b * c;
}
console.log(func.length); // 3 (매개변수의 개수는 3개)
name 프로퍼티
함수 객체의 name 프로퍼티는 말 그대로 함수의 이름(name)을 가리키는 속성이다. 여기서 주의할 점은, 만일 함수의 이름이 없는 익명 함수일 경우, name 프로퍼티 값은 그 함수 객체를 가리키는 식별자의 이름(익명 함수를 변수에 담았다면, 변수의 이름이 식별자의 이름이다)을 가리킨다. 다만, ES6에서 정식 표준이 된 프로퍼티이기에 ES5에서는 익명 함수일 경우 빈 문자열이 name 프로퍼티의 값이 된다.
const multiFunc = function func(a, b, c) {
return a * b * c;
};
console.log(multiFunc.name); // func
const annoymousFunc = function() {};
console.log(annoymousFunc.name); // annoymousFunc
// ES5일 경우 빈 문자열이 name 프로퍼티 값이 된다.
나머지 두 프로퍼티(__proto__ 접근자 프로퍼티, prototype 프로퍼티)는 프로토타입 개념을 알아야 하므로, 이번 포스트에서는 생략한다.
더 알아보기: 다른 언어는 일급 함수를 지원할까?
그렇다면 일급 함수가 다른 언어에서는 지원할까? 지원한다면 그 이유는 무엇일까?
대표적으로 자바스크립트, 파이썬, C언어, Java 4가지의 언어에서 일급 함수를 지원하는지 간단히 알아보자. 먼저 일급 함수의 장점은 무엇일까. 함수를 변수에 넣어 다룰 수 있다면, 아무래도 함수를 더 유연하게 관리하고 사용할 수 있게 될 것이다. 그렇다면 당연히 코드 작성도 좀 더 편해질 것이다.
먼저 자바스크립트는 위에서 설명했듯 일급 함수를 지원한다. 자바스크립트는 동적이고 유연한 웹 개발을 위해 설계된 언어이기에, 함수가 객체로 취급되면 복잡한 비동기 작업과 콜백 패턴을 효과적으로 처리할 수 있도록 해준다는 강력한 이점이 있다. 파이썬 역시 읽기 쉽고 직관적인 코드를 강조하며, 다양한 프로그래밍 패러다임(절차적, 객체지향, 함수형)을 지원한다. 함수 또한 객체로 취급되어 유연한 코드 작성을 도와준다.
반면에 C언어와 Java는 함수 자체를 값으로 다루는 것이 어렵다고 한다. 첫 번째로, C언어는 저수준 시스템 프로그래밍 언어를 위해 설계된 언어이다. 그렇다 보니 성능과 효율성이 강조될 수밖에 없어 전반적이고 정적이고 명시적인 코드 작성을 중시한다. 다만 함수 포인터를 통해 동적으로 구현이 일부 가능하긴 한다. 다음으로 Java는 안전성과 유지보수가 강조되는 객체지향 언어이다 보니 일급 함수를 지원하는 것이 어렵다고 한다. 다만 최근에 Java 8부터는 함수형 인터페이스를 도입하여 함수형 프로그래밍을 일부 지원해준다고 한다. 그렇지만 여전히 자바에서의 함수는 객체가 아니다.
이렇듯 각 언어에서 함수의 취급은 언어의 설계 철학과 목적에 크게 의존한다.
'◼ FrontEnd > JavaScript' 카테고리의 다른 글
[JavaScript] forEach문에서 return이나 break를 쓰면 안된다 (3) | 2023.10.27 |
---|