티스토리 뷰
▶클로저(closure)
클로저(closure)는 함수의 랙시컬 스코프를 기억하는 함수가 렉시컬 스코프를 벗어난 외부 스코프에서 실행될 때에도 자신의 렉시컬 스코프에 접근할 수 있게 해주는 것입니다.
클로저는 자바스크립트에서 굉장히 중요한 개념이며, 함수를 사용하는 곳이면 어디든지 적용할 수 있습니다.
클로저를 활용하면 특정한 상태를 기억하고 캡슐화하거나 나아가 하나의 모듈을 정의하는 패턴으로도 확장할 수 있습니다.
이렇게 강력한 활용도를 가진 클로저는 앞서 설명한 렉시컬 스코프와 스코프 체인(이하 렉시컬 스코프 체인)만 제대로 이해하면 쉽게 사용할 수 있습니다.
function foo() {
var a = 1;
function bar() {
console.log(a); // 1
}
bar();
}
foo();
bar() 함수는 상위 스코프인 foo() 함수 내에서 실행되며, 렉시컬 스코프 체인을 통해 foo() 함수의 스코프를 기억하고 있습니다.
그렇다면 bar() 함수를 클로저라고 볼 수 있을까요?
이론적으로 보면 그렇다고 할 수 있지만 실제로 이런 경우를 클로저라고 부르지는 않습니다.
일반적으로 bar()와 같은 내부 함수가 자신을 감싸고 있는 외부 함수(foo())를 벗어나 완전히 독립적인 스코프에서 실행되었을 경우 클로저라고 부릅니다.
위 코드를 클로저를 이용한 코드로 변경해 보겠습니다.
function foo() {
var a = 1;
function bar() {
console.log(a); // 1
}
return bar;
}
const baz = foo();
baz(); // 1
코드는 아래와 같은 순서대로 실행됩니다.
1단계 bar() 함수는 렉시컬 스코프 체인을 통해 foo() 함수의 스코프를 기억합니다.
2단계 bar() 함수를 전역 변수 baz에 할당하였습니다.
3단계 전역 변수 baz를 사용하여 bar() 함수를 호출하였습니다.
4단게 bar() 함수는 자신의 스코프에서 변수 a를 찾습니다.
5단계 자신의 스코프에서 찾을 수 없기 때문에 스코프 체인을 통해 foo() 함수의 스코프에서 찾습니다.
6단계 foo() 함수의 스코프에서 변수 a를 찾아 1을 출력합니다.
bar() 함수는 자신이 생성된 렉시컬 스코프에서 벗어나 baz라는 전역 변수로 호출되었습니다.
여기서 중요한 점은 bar() 함수가 지산을 감싸고 있는 foo() 함수를 벗어나 bar() 함수의 스코프와 상관 없는 전역 스코프에서 실행된다는 점입니다.
그리고 bar() 함수를 실행하였을 때 자신의 렉시컬 스코프 체인을 통해 foo() 함수의 스코프에서 변수 a를 찾습니다.
이것이 바로 클로저입니다.
또한 전역 스코프가 아닌 어느 곳에서 호출되어도 bar() 함수는 기억한 렉시컬 스코프 체인을 통해 변수 a를 찾을 수 있습니다.
즉 클로저를 사용하면 외부에서도 얼마든지 원래의 렉시컬 스코프에 접근할 수 있습니다.
function foo() {
var a = 1;
function bar() {
console.log(a); // 1
}
return bar;
}
function baz() {
const fn = foo();
fn(); // 1
}
baz();
전역 스코프가 아닌 baz() 함수의 내부에서 bar() 함수를 호출하여도 클로저에 의해 bar() 함수의 렉시컬 스코프에 접근할 수 있습니다.
1) 모듈 패턴
클로저를 활용하면 모듈을 정의하여 원하는 프로퍼티나 메서드를 캡슐화할 수 있습니다.
ES2015 이전에 자바스크립트에는 모듈이라는 개념이 없었기 때문에 클로저를 사용하여 모듈을 정의하여 사용하였습니다.
[NOTE]
클로저로 모듈을 생성하지 않고 전역 스코프에 필요한 값들을 정의해도 됩니다.
하지만 전역 변수에 정의된 값들이 많아지면 사용하는 전역 변수가 어디서 선언되었는지 찾기 어렵고 다른 라이브러리와 변수명이 충돌할 수도 있습니다.
최악의 경우에는 어딘가에서 전역 변수의 값이 덮어씌워져 애플리케이션이 제대로 동작하지 않을 수도 있습니다.
애플리케이션에 잠재적인 버그를 심고 싶지 않다면 전역 스코프를 오염시키지 않는 것이 좋습니다.
function myModule() {
let counter = 0;\
function increment() {
counter += 1;
}
function decrement() {
counter -= 1;
}
function getCount() {
return counter;
}
return {
increment,
decrement,
getCount
}
}
const myCounter = myModule();
myCounter.increment();
console.log(myCounter.getCount()); // 1
myCounter.decrement();
console.log(myCounter.getCount()); // 0
myModule() 함수는 increment(), decrement,(), getCount() 함수들을 객체로 만들어 실행 결과로써 반환합니다.
반환된 함수들은 기억한 렉시컬 스코프 체인에 의해 myModule() 함수의 스코프에 접근할 수 있습니다.
그렇기 때문에 세 함수를 사용하면 외부 스코프에서도 myModule() 함수 내부에 선언된 counter 변수에 접근하여 값을 변경하거나 조회하는 것이 가능합니다.
그리고 여기서 중요한 점은 myModule() 함수가 반환한 객체는 함수들에 대한 참조만 가지며 내부 변수 counter에 대한 접근은 불가능하다는 점입니다.
즉 counter 변수는 캡슐화되어 외부에서 접근할 수 없으며, 접근하고 싶다면 외부로 반환한 클로저 함수를 통해서만 접근할 수 있습니다.
이것이 클로저를 활용한 모듈 패턴입니다.
ES2015에서 모듈 명세가 등장했기 때문에 현재는 클로저를 사용하여 모듈 패턴을 구현하는 경우는 거의 없습니다.
하지만 IE와 같은 레거시 브라우저는 ES2015의 모듈을 지원하지 않기 때문에 레거지 브라우저 지원을 위해서는 이러한 패턴을 이해가고 사용할 수 있어야 합니다.
또한 모듈 패턴은 클로저를 활용한 아주 좋은 예시이니 이 패턴의 원리에 대해서는 꼭 숙지하길 바랍니다.
▷즉시 실행 함수 표현식
즉시 실행 함수 표현식(IIFE, Immediately Invoked Function Expression)은 정의되자마자 즉시 실행되는 함수이며, 익명 함수를 응용한 패턴입니다.
즉시 실행 함수는 익명 함수를 괄호(())로 둘러싼 형태로 정의합니다.
(function (lang) {
// ...
}){'javascript');
function (lang) {} 을 괄호로 둘러 쌓고, 함수를 바로 실행하기 위해 괄호(())를 추가하였습니다.
그리고 'javascript' 문자열을 인자로 넘겨 즉시 실행 함수의 매개변수 lang으로 사용하였습니다.
즉시 실행 함수를 모듈 패턴과 함께 사용하면 전역 스코프를 오염시키지 않고 모듈 객체를 만들 수도 있습니다.
const cleanModule = (function () {
let counter = 0;
funtion increment() {
counter += 1;
}
function decrement() {
counter -= 1;
}
function getCount() {
return counter;
}
return {
increment,
decrement,
getCount
}
})();
myModule() 함수를 즉시 실행 함수로 변경하여 바로 실행하였습니다.
그리고 실행 결과로 반환된 모듈 객체를 cleanModule 변수에 할당하였습니다.
이렇게 즉시 실행 함수와 모듈 패턴을 함께 사용하면 전역 스코프를 오염시키지 않고도 myModule()과 같은 모듈 함수를 선언하여 객체를 생성할 수 있습니다.

여기까지 자바스크립트 클로저에 대해 알아보았습니다 :)
'프론트엔드 > Javascript' 카테고리의 다른 글
[Javascript] ⑬ 자바스크립트 var, let, const (1) | 2022.10.03 |
---|---|
[Javascript] ⑫ 자바스크립트 모듈 (0) | 2022.07.25 |
[Javascript] ⑩ 자바스크립트 호이스팅(Hoisting) (0) | 2022.07.25 |
[Javascript] ⑨ 자바스크립트 스코프(Scope) (0) | 2022.07.25 |
[Javascript] ⑧ 자바스크립트 프로토타입 🌟🌟🌟 (0) | 2022.07.25 |
- Total
- Today
- Yesterday
- CSS
- 프론트엔드 기초
- JSP
- TypeScript
- 리액트 훅
- 디프만
- 스타일 컴포넌트 styled-components
- testing
- 자바
- next.js
- 자바스크립트
- rtl
- 프로젝트 회고
- react
- 프론트엔드 공부
- Python
- HTML
- frontend
- 파이썬
- 자바스크립트 기초
- 인프런
- 머신러닝
- 타입스크립트
- 프론트엔드
- 딥러닝
- react-query
- 리액트
- jest
- 데이터분석
- styled-components
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |