Javascript에서 왜 함수가 1급 객체일까요?

최근 Javascript에서 함수가 왜 1급 객체(first class citizen)인지 의논해 보는 자리가 있었는데 잘 알지 못하는 것 같아 공부한 내용을 정리합니다.

일단 말 그대로 first class citizen, second class citizen 이란 무엇일까요? first class citizen이란 자유롭게 거주하고 일 할 수 있고, 출입국의 자유를 가지며, 투표의 자유를 가지는 시민을 의미한다면, second class citizen이란 시민 또는 합법적 거주자이지만 시민권 및 사회 경제적 기회가 제한되어있는 시민을 의미합니다. 예를 들면, 19세기 초 참정권을 가진 남성이 first class citizen이라면, 참정권을 가지지 못했던 여성은 second class citizen이라고 할 수 있는 것이죠.

프로그래밍 세계에서도 비슷한 개념이 존재합니다.

프로그래밍 언어 디자인에서, 특정 언어의 first-class citizens(first-class type, 또는 first-class object, 또는 first-class value라고도 할수 있는데)이란 보통 다른 객체들에게 적용 가능한 연산을 모두 지원하는 객체를 말합니다. 이러한 연산에는 보통 매개변수로 전달되고, 함수에서 반환되고 수정되고 변수에 할당되는 작업이 포함되는데요.

1급 객체, 2급 객체에 대한 개념은 1960년대에 Christopher Strachey에 의해 소개 되었고, 사실상 그 용어를 명시적으로 정의한 것은 아니지만 Algol 라는 프로그래밍 언어의 실수(Real number)와 프로시저를 비교함으로써 1급 객체의 개념에 대해 처음으로 언급하였다고 합니다. 1990년대에는 미국의 컴퓨터 과학자 Raphael Finkel 이 2급, 3급 객체에 대한 정의를 제안했는데, 널리 받아 들여지고 있지 않은 상황이라고 해요.

그러니까 즉! 프로그래밍 언어에서 type을 전달, 반환 및 할당 할 수 있는 경우 해당 type을 1급 객체로 간주됩니다. Javascript에서 함수를 반환할 수 있을 뿐만 아니라 함수를 받을 수 있는 함수를 만들수 있으니 함수형 프로그래밍으로 Javascript가 인기가 있어지는 이유이도 합니다. 그래서 하나 이상의 함수를 인수로 받거나 함수를 반환하는 고차 함수를 만들 수 있어요.

first-class citizen의 조건은 다음과 같아요.

  • 변수나 데이터 구조안에 담을 수 있다.
  • 파라미터로 전달 할 수 있다.
  • 리턴 값으로 사용할 수 있다.

그럼 몇가지 예를 하나씩 살펴 볼까요?

1.함수 할당

Javascript에서는 여러 가지 방법으로 변수에 함수를 할당 할 수 있습니다.

1) var 키워드 이용하기

var a = function() {
//function body...
};

일반적인 패턴은 변수에 함수를 저장하기 위해 var 키워드를 사용하여 함수를 정의하는 것입니다. 여기서 익명 함수를 사용하지만 원하는 경우 아래 예처럼 명명 함수를 사용할 수도 있습니다.

var a = function b() {
//function body...
};

2) method 이용하기

Javascript에서 method를 할당하는 것은 매우 쉬운데요. 함수에 변수를 할당하는 것과 마찬가지로 함수를 객체의 키값으로 할당 할 수 있습니다.

var movie = {
name: '옥자',
director: '봉준호',
show: function() {
console.log(this.name + ' ' + this.director);
}
}

다음과 같은 함수를 만들 때는

var add = function(a,b) {
return a + b;
}

add 변수 안에 저장된 값은 함수이므로, 함수를 호출하지 않고 그 값을 아래처럼 전달할 수 있어요.

var newAdd = add;
newAdd(2,3) //5

2.함수 전달하기

함수는 Javascript의 1급 객체이기 때문에 전달할 수 있습니다. 아래의 예가 있다고 가정해 보죠. 각각의 이벤트가 발생할때 똑같은 기능을 수행하고 있는데요.

$('form').on('submit',function() {
//show specific text
});
$('a').on('click', function() {
//show specific text
});

반복적인 부분이 있고 개선하고 싶을때 반복을 줄이고 사용자가 해당 요소와 상호 작용할 때 하나의 함수가 실행되도록 전달할 수 있습니다. 아래의 예 처럼요!

function showText(e) {
//show specific text
}
$('form').on('submit',showText);
$('a').on('click',showText);

3. 함수 반환하기

마지막으로 함수에서 함수를 반환하는 것입니다. 이 개념이 익숙해지면 무척 강력한 개념이 될 수 있는데요. 함수형 프로그래밍과 관련하여 중요한 개념입니다. Node.js의 require 문을 사용하여 스크립트를 불러올수 있는데, helmet을 예로 들어보겠습니다.

var helmet = require('helmet')

Node.js에서 모듈을 작성할 때 여러가지를 반환 할 수 있지만 단순히 함수를 반환하는 것이 일반적입니다. 위의 예제에서 require 호출에서 반환 된 값은 함수입니다.

다음은 helmet의 index.js 에서 가져온 부분입니다.

function helmet (options) {
options = options || {}

if (options.constructor.name === 'IncomingMessage') {
throw new Error('It appears you have done something like `app.use(helmet)`, but it should be `app.use(helmet())`.')
}

var chain = connect()

middlewares.forEach(function (middlewareName) {
var middleware = helmet[middlewareName]
var middlewareOptions = options[middlewareName]
var isDefault = DEFAULT_MIDDLEWARE.indexOf(middlewareName) !== -1

if (middlewareOptions === false) {
return
} else if (middlewareOptions === true) {
middlewareOptions = {}
}

if (middlewareOptions != null) {
chain.use(middleware(middlewareOptions))
} else if (isDefault) {
chain.use(middleware({}))
}
})

return chain
}
module.exports = helmet

이 예제는 module.exports를 사용하여 함수를 반환하는데, 그 자체가 함수 호출의 결과를 반환합니다. 이렇게 하면 아래와 같이 사용할 수 있어요 :)

app.use(helmet())

Partial application

함수를 반환하는 또 다른 일반적인 용도는 partial application입니다. partial application은 “함수를 반환하는 함수”를 만드는 데 사용됩니다. 첫 번째 함수를 호출하면 전달 된 첫 번째 매개변수가 저장되는데, 간단한 예제를 살펴볼까요?

function add(a) {
return function(b) {
return a + b;
}
}

이 함수는 a 매개 변수를 가지고 있다가 두 번째 매개 변수 b를 취하는 함수를 반환합니다. a는 반환 된 함수가 생성 된 클로저 때문에 두 번째 내부 함수에서 그 값을 기억하는데 어떻게 사용하는 지 살펴 볼게요:)

var add5 = add(5);

add5에 저장된 값은 함수입니다. 그 함수 안에는 a가 참조되며 생성 될 때 a가 사용 가능하기 때문에 기억을 하는데요. a는 5가 되겠죠!

function(b) {
return a + b;
}

그리고 add5를 호출하면서 새로운 value에 5를 더해서 아래와 같은 결과가 나오게 됩니다.

add5(5); // 10
add5(8); // 13

마지막으로 정리해 보자면..

Javascript에서 함수는 아래 3 가지조건을 충족하니 1급 객체라고 할수 있는 것입니다!

  1. 함수를 변수나 데이타에 할당 할 수 있다.
  2. 함수를 인자로 전달 할 수 있다.
  3. 함수를 리턴 할수 있다.

일단 제가 이해한 방식으로 정리를 해 보았는데요! 잘못된 부분이 있거나, 개선해야 되는 점이 있으면 피드백 주세요! :)

reference: https://en.wikipedia.org/wiki/First-class_citizen

http://ryanchristiani.com/functions-as-first-class-citizens-in-javascript/