1. 일급 객체란?
여기서 일급이라는 것은 일급이라서 어떠한 혜택을 받는다는 것이 아니라, 사용할 때 다른 요소들과 다른 것이 없다는 것을 뜻한다고 한다. 즉, 일급 객체란 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 말한다.
일급객체가 되기 위해서는 다음과 같은 조건들을 만족해야하는데, 어떠한 조건이 있는지 알아보자.
|
각각의 조건을 조금 더 자세하게 살펴보면서 자바스크립트의 함수는 왜 일급객체인지, 그 부분을 알아보려고 한다.
2. 자바스크립트의 함수는 일급객체?
1) 모든 일급객체는 변수에 할당할 수 있다.
가장 먼저 모든 일급객체는 변수에 할당할 수 있다라는 조건을 살펴보려고 하는데 자바스크립트 함수를 어떻게 사용했었는지를 생각해보면 금방 답이 나온다. 자바스크립트는 함수 선언식, 함수 표현식, 화살표 함수 등으로 다양하게 활용되고 있는데 그 중에서 함수 표현식을 생각해보면 변수에 자유롭게 대입이 가능하다는 것을 알 수 있다.
const hello = function() {
console.log("Hello!");
}
2) 모든 일급객체는 다른 함수의 인자로 전달할 수 있다.
이의 활용 예시로는 콜백함수를 말할 수 있는데, 자바스크립트는 콜백함수 형태로 함수의 파라미터로 넘길 수 있다.
const hello = function() {
console.log("Hello!");
}
function start(func) {
func();
}
start(hello); // Hello!
3) 모든 일급객체는 다른 함수의 결과로서 리턴될 수 있다.
자바스크립트는 클로저라는 기법이 존재하기 때문에 이를 통해 함수의 리턴값으로 구성될 수 있다. 즉, 클로저가 함수의 일급객체 성질을 이용한다.
const hello = function() {
console.log("Hello!");
return function() {
console.log("Hello World!");
}
}
const hello2 = hello(); // Hello!
hello2(); // Hello World!
이렇게 일급 객체의 조건을 충족하는 자바스크립트의 함수를 살펴보았는데 자바스크립트 함수의 특징을 다시 한 번 정리하면 다음과 같다.
|
3. 자바스크립트 클로저 차근차근 이해해보기
자바스크립트를 공부하다보면 자연스럽게 클로저라는 개념을 자주 접하게 되는데 이를 정확하게 이해하고 있는 사람은 많이 없는 것 같다. 들어는 봤지만 설명하기는 애매한 그런 느낌인 것 같아 이번 기회에 조금 자세하게 공부해보고자한다.
클로저는 자바스크립트의 고유 개념이라기보단 함수를 일급객체로 취급하는 함수형 프로그래밍 언어에서 사용되는 중요한 특성이라고 할 수 있다. 보통 "함수와 그 함수가 선언되었을 당시의 렉시컬 환경"이라고 말하는데 이렇게만 들어서는 어떤 것을 의미하는 것인지 알기 애매모호한 것 같다.
1) 렉시컬 환경, 렉시컬 스코프란?
먼저 아래의 간단한 예제를 살펴보면 외부함수 outer과 내부함수 inner가 있다. 이 때, 내부함수는 외부함수의 변수 x를 참조할 수 있다. 이는 스코프 개념으로도 설명해볼 수 있는데 렉시컬 스코프는 함수를 호출할 때가 아니라 함수를 어디에 선언했는지에 따라 결정된다.
function outer() {
const x = 20;
const inner = function () { console.log(x); };
inner();
}
outer(); // 20
내부 함수인 inner는 외부 함수인 outer 안에 선언되었기 때문에, inner의 상위 스코프는 outer가 되고 outer의 상위 스코프는 전역 스코프가 된다. 내부 함수 inner가 외부 함수 outer 안에 선언되었기 때문에 inner는 자신이 속한 렉시컬 스코프(전역, 외부함수, 자신의 스코프)를 참조할 수 있게 된다.
여기서 렉시컬 스코프에 대한 개념도 명확해졌다. 렉시컬 스코프는 함수를 호출할 때가 아니라 함수를 어디에서 선언하였는지에 따라 결정되는 스코프로 해당 함수가 참조할 수 있는 범위를 뜻하는 것 같다. 이제 이를 실행 컨텍스트 관점에서 알아보아야할 것 같다.
2) 실행 컨텍스트와 스코프 체인
내부함수 inner가 호출되면 자신의 실행 컨텍스트가 실행 컨텍스트 스택에 쌓이고 변수 객체와 스코프 체인, this에 바인딩할 객체가 결정된다. 즉, inner 함수가 outer 함수의 변수를 참조할 수 있었던 것은 실행 컨텍스트의 스코프 체인을 자바스크립트 엔진이 검색했기 때문에 가능한 것이다. 자세한 원리는 아래와 같다.
<inner 함수 실행>
inner 함수 실행 -> inner 함수가 자신의 스코프 내에서 변수 x를 검색 -> 검색에 실패 -> inner 함수를 포함하는 외부 함수 outer의 스코프에서 변수 x를 검색 -> 검색에 성공 |
3) 클로저의 개념 등장
이전 예제는 내부함수를 외부함수 내에서 호출하도록 했지만, 이번에는 호출이 아닌 반환을 해보도록 해보려고 한다.
function outer() {
const x = 10;
const inner = function () { console.log(x); };
return inner;
}
const inner = outer();
inner(); // 10
해당 코드에서 즉시실행함수인 outer 함수는 inner 함수를 반환하며 생을 마감한다. 즉, 외부함수 outer을 호출하면 내부함수 inner를 반환하고, 함수 outer의 실행 컨텍스트가 소멸된다. 이렇게만 봤을 때는 outer 함수의 콜스택이 제거되면서 outer 함수 내의 변수 x 또한 더이상 유효하지 않게 되어 x에 접근할 방법이 없을 것 같다. 하지만 이를 실행시켜보면 x에 할당되어있는 값인 10이 호출된다.
이처럼 내부함수가 외부함수보다 더 오래 유지되는 경우, 외부 함수 밖에서 내부함수가 호출되더라도 외부함수의 지역 변수에 접근할 수 있다고 하는데 이러한 함수를 클로저라 한다고 한다. 이렇게 함수, 렉시컬환경, 실행컨텍스트까지 모두 확인하면서 클로저의 개념까지 도달했는데, 이를 바탕으로 위에서 정의한 것을 다시 짚어보자면 클로저는 "반환된 내부함수가 자신이 선언되었을 당시의 환경인 렉시컬 환경을 기억하여 렉시컬 스코프 밖에서 호출되어도 그 환경에 접근할 수 있는 함수"를 의미하는 것이다. 이를 축약한 표현이 "자신이 생성될 때의 환경을 기억하는 함수"인 것이다.
그냥 정의만 암기했을 때는 몰랐던 부분이 조금씩 이해되기 시작했다. 아직까지도 완전하게 이해된 것은 아니지만 그래도 하나하나 배워가면서 이해되기 시작하니 나중에는 이를 어떻게 활용하면 좋을지도 고민해보면 좋을 것 같다. 해당 부분은 모던 자바스크립트 딥 다이브 책으로 일급객체와 클로저의 개념을 공부하다가 자세하게 찾아보게 되었는데, poiemaweb 사이트를 통해 정말 많은 도움을 받은 것 같다. 해당 사이트를 참고하여 정리했기에 좀 더 자세하게 공부해보고 싶다면 해당 사이트 추천!!!