Closure
클로저는 함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다.
이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.
자바스크립트는 함수가 호출되는 환경과 별개로, 기존에 선언되어 있던 어휘적 환경을 기준으로 변수를 조회하려고 한다.
"외부함수의 변수에 접근할 수 있는 내부함수" 를 클로저 함수로 부르는 이유도 그렇다.
Closure 함수의 특징
- 함수를 리턴하는 함수
const adder = function (x) {
return function (y) {
return x + y;
};
};
- 리턴하는 함수에 의해 스코프가 구분됨
※ 클로저의 핵심 : 스코프를 이용해서 변수의 접근 범위를 닫음(closure)
→ 외부 함수의 변수에 내부 함수가 접근할 수 있음
Closure의 활용
데이터를 보존하는 함수
일반적인 함수는 함수 실행이 끝나고 나면 함수 내부의 변수를 사용할 수 없다.
하지만 클로저는 외부 함수의 실행이 끝나더라도 외부 함수 내 변수가 메모리 상에 저장된다. (어휘적 환경을 메모리에 저장)
const adder = function (x) {
return function (y) {
return x + y;
};
};
const add5 = adder(5);
add(7); // 12
add(10); // 15
const doubleAdd = adder(2)(4);
위 예시코드에서 변수 add5에는 클로저를 리턴한 함수가 담겨 있다.
add5는 외부 adder 함수의 실행이 끝났음에도 adder 함수에서 인자로 넘긴 5라는 값을 x 변수에 계속 담은 채로 남아있다.
adder(2)(4)를 할당한 doubleAdd를 추가해보았다.
이런 때에는 x와 y에 순차적으로 값을 넣어줄 수 있게 된다.
const tagMaker = (tag) => (content) => `<${tag}>${content}</${tag}>`;
const divMaker = tagMaker("div");
divMaker("hello"); // '<div>hello</div>'
divMaker("codestates"); // '<div>codestates</div>'
const anchotMaker = tagMaker("a");
anchorMaker("go");
anchorMaker("urclass");
위 코드와 같이 클로저를 HTML 태그 및 문자열을 만들기 위해서 실용적으로 활용해볼 수 있다.
클로저는 이처럼 특정 데이터를 스코프 안에 가두어 둔 채로 계속 사용할 수 있게 해준다.
클로저 모듈 패턴
const makeCounter = () => {
let value = 0;
return {
increase: () => {
value = value + 1;
},
decrease: () => {
value = value - 1;
},
getValue: () => value,
};
};
const counter1 = makeCounter();
counter1; // { increase: f, decrease: f, getValue: f }
클로저를 이용해서 내부 함수를 단 하나만 리턴하는 것이 아니라 객체에 담아 여러 개의 함수를 리턴하도록 만들 수 있다.
makeCounter() 함수를 실행하여 변수를 담고서 확인해보면 객체가 리턴되는 것을 확인할 수 있다.
또 한가지 유념해야 할 것은, 위쪽에 선언된 value라는 변수는 직접 수정이 불가능하다는 점이다.
"외부 스코프에서는 내부 스코프의 변수에 접근할 수 없다"는 규칙에 의해, 어떤 경우에도 value는 직접 수정이 불가능하다.
대신 리턴하는 객체가 제공하는 메소드를 통해 value 값을 간접적으로 조작할 수 있다.
이것이 바로 정보의 접근 제한(캡술화) 이다.
const counter1 = makeCounter();
counter1.increase();
counter1.increase();
counter1.decrease();
counter1.getValue(); // 1
const counter2 = makeCounter();
counter2.decrease();
counter2.decrease();
counter2.decrease();
counter2.getValue(); // -3
위에서 counter1과 counter2의 getValue 값을 확인해보면 서로 다른 value 값을 가지고 있음을 확인할 수 있다.
makeCounter에 의해 리턴된 두 객체는 이렇게 서로에게 영향을 끼치지 않고 각각의 값을 보존할 수 있다.
이와 같이 함수 재사용성을 극대화하여 함수 하나를 완전히 독립적인 부품 형태로 분리하는 것을 모듈화 라고 한다.
클로저는 이처럼 데이터와 메소드를 같이 묶어서 다룰 수 있다.
즉, 클로저는 모듈화에 유리하다.
유튜브 영상을 통해 Closure 쉽게 이해하기
이대로 넘어가기에는 아직 학습이 덜 된 것 같기에, 유튜브에서 Taehoon님의 클로저 설명 을 보고 다시 정리해보았다.
- 정말 간단하게 보자면, 외부 변수와 함수를 합쳐서 Closure라고 부른다.
- Closure가 어렵게 느껴진다면 그건 내 탓이 아니라 JavaScript 때문.
사실상 모든 함수는 Closure 함수가 될 수 있다. 주변에 다 Closure 밖에 없는데 뭐가 Closure고 뭐가 Closure가 아닌지 구별하려고 하니 어려움이 생기는 것.
let poison = 0;
@capture(poison)
function add(a, b) {
return a + b + poison;
}
pure function add1(a, b) {
return a+ b;
}
let poison = 0;
function add2(a, b) {
return a + b + poison;
}
다른 많은 프로그래밍 언어들에서는 Closure 함수와 일반 함수가 명확한 구분을 가지고 있는 경우가 많다.
위 예시 코드는 다른 프로그래밍 언어의 Closure 함수에 대한 예시를 이해하기 쉽게 임의로 만들어둔 것이다.
기본적으로 모든 함수가 Closure 기능을 쓸 수 있다는 것은 사용에는 편리하지만 이해하는 데에는 완벽한 정보가 제공되지 않기 때문에 어려움이 있을 수 있다.
알아야 할 개념들은 Script 언어라고 해서 적은 게 아니다.
처음 봤을 때 이러한 개념들이 뒤에 숨어있기 때문에 쉬워보이는 것 뿐이다.
'JavaScript > Vanilla' 카테고리의 다른 글
객체의 얕은 복사와 깊은 복사 (0) | 2021.07.24 |
---|---|
Hoisting (0) | 2021.07.24 |
Spread와 Rest 문법 (0) | 2021.07.24 |
let / const / var 그리고 scope (0) | 2021.07.18 |
JavaScript의 탄생 배경 (0) | 2021.07.17 |