클로저(Closure)?
함수가 선언된 환경의 렉시컬 스코프를 기억하여 함수가 스코프 밖에서 실행될 때에도 이 스코프에 접근할 수 있게 하는 기술
클로저는 바깥 합수의 변수에 접근할 수 있는 중첩함수이다.
클로저란 다른 함수의 스코프에 있는 변수에 접근 가능한 함수
어휘적 범위 지정 (Lexical Scoping) 예제 1
function outer(param){
const outerParam = `Hello ${param}!`;
function inner() {
console.log(outerParam); // 부모 함수에서 선언된 변수 outerParam를 사용
}
return inner;
}
const closure = outer('클로저');
closure(); // Hello 클로저!
inner 함수에서 outerParam을 접근 할 수 있다.
비슷한 다른 예제도 살펴보자.
어휘적 범위 지정 (Lexical Scoping) 예제 2
function init() {
const name = "홍길동";
function displayName() { // displayName()은 내부 함수이며, 클로저이다.
console.log(name); // 부모 함수에서 선언된 변수를 사용한다.
}
displayName();
}
init(); // 홍길동
마찬가지로 displayName 함수에서 name 이라는 변수의 값을 출력했다.
이 성질을 이용하여 스코프 밖에서도 실행되게 하는 함수가 Closure이다.
클로저 예시
위에 만들었던 init()을 수정하여 클로저를 만들어보겠다.
function init() {
const name = "홍길동";
function displayName() {
console.log(name)
}
return displayName; // 전 예제와 다른 점(함수를 리턴한다)
}
const myInit = init(); // displayName을 리턴
myInit(); // name 변수에 접근
전 예제와 다른점은 함수를 호출하는 것이 아닌 리턴한다는 점이다.
결과값으로는 홍길동이라는 name 변수 값이 conosle창에 입력된다.
function init(name) {
function displayName() {
console.log(name)
}
return displayName;
}
const myInit = init("홍길동");
myInit(); // 홍길동
위 예제 처럼 name을 지정해줄 수 있다.
또한 OOP처럼 getter, setter를 지정해 줄 수 있다.
function init() {
const name = "김길동"
function displayName() {
console.log(name);
}
return {
getName : function() {
return name;
},
setName : function(newName) {
name = newName;
}
};
}
const myName = init();
myName.setName("홍길동");
console.log(myName.getName()); // 홍길동
return에 JSON형식으로 {함수명: 함수}로 작성할 수 있다.
클로저의 한계
클로저는 항상 외부 함수의 변수에 마지막으로 저장된 값만 알 수 있다.
클로저가 특정 변수가 아니라 전체 변수 객체에 대한 참조를 저장하기 때문이다.
예시를 보자.
function idCreater(list) {
const i;
const unique = 100;
for (i = 0; i < list.length; i++) {
list[i]["id"] = function () {
return unique + i;
}
}
return list;
}
const list = [{name : "레레", id : 0}, {name : "도도", id : 0}, {name : "미미", id : 0}];
const idList = idCreater(list);
console.log(idList[0].id()); // 103
정상적으로 출력하면 idlList[0].id()에 100이 들어가야 맞는데 출력을 해보면 103이 됨을 알 수 있다.
이러한 클로저의 한계를 해결하기 위해 IIFE를 사용한다.
[Javascript] IIFE (즉시 실행 함수)
IIFE란? IIFE(Immediately-Invoked Function Expressions) 정의되자마자 즉시 실행되는 함수 표현식 사용이유? 전역 스코프에 불핑요한 변수의 추가를 방지하고 IIFE 내부에 다른 변수들이 접근하는것을 막을 수
datobi.tistory.com
앞서 봤던 코드에 즉시 실행 함수를 적용시켜보자.
function idCreater(list) {
const i;
const unique = 100;
for (i = 0; i < list.length; i++) {
list[i]["id"] = (function () {
return unique + i;
})();
}
return list;
}
const list = [{name : "레레", id : 0}, {name : "도도", id : 0}, {name : "미미", id : 0}];
const idList = idCreater(list);
console.log(idList[0].id); // 100
100으로 올바른 값이 나옴을 알 수 있다.
클로저 모듈 패턴
이와 비슷하게 IIFE로 클로저를 감싸는 것을 클로저 모듈 패턴이라 하며 이 모듈패턴을 이용하면 클로저를 private 메소드처럼 흉내낼 수 있다.
프라이빗 메소드는 코드에 제한적인 접근만을 허용하고 불필요한 메소드가 공용 공간에 접근할 수 없게 만든다. (캡슐화, 은닉화)
이러한 정점덕에 클로져를 이런 모델로 사용하는것이 보편적이다.
패턴의 기본 틀은 이러하다.
const counter = (function() { // const counter = (() => { 이렇게 화살표함수도 가능
var num = 0;
function changeBy(val) {
num += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return num;
}
};
})();
console.log(counter.value()); // 0
counter.increment();
counter.increment();
console.log(counter.value()); // 2
counter.decrement();
console.log(counter.value()); // 1
위 예제에는 두개의 프라이빗 아이템이 있다. 하나는 num이고 changeBy라는 함수이다. 둘 다 익명함수 외부에서는 접근할 수 없다. 그러나 클로저 안에서는 접근이 가능하다.
독립성을 유지하는지 테스트해보자.
const counter = function() {
var num = 0;
function changeBy(val) {
num += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return num;
}
};
}
const counter1 = counter();
const counter2 = counter();
console.log(counter1.value()); /* 0 */
counter1.increment();
counter1.increment();
console.log(counter1.value()); /* 2 */
counter1.decrement();
console.log(counter1.value()); /* 1 */
console.log(counter2.value()); /* 0 */
독립성을 잘 유지하는 것을 볼 수 있다.
나중에 실용적인 예들을 조금 더 살펴보겠다.
헷갈렸던 점
클로저를 스터디 하기 전, 클로저 안에서 getter/setter를 이용해야 하는지 여부가 궁금했었다.
해답은 클로저 외부에서 변수를 참조할때는 함수로 접근해야만 하고
클로저 내부에서 변수를 참조할때에는 그냥 참조해도 상관없다는 것!
Reference
https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures
클로저 - JavaScript | MDN
클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.
developer.mozilla.org
https://javascriptissexy.com/tag/closures/
Closures – JavaScript Is Sexy
Closures allow JavaScript programmers to write better code. Creative, expressive, and concise. We frequently use closures in JavaScript, and, no matter your JavaScript experience, you will undoubtedly encounter them time and again. Sure, closures might app
javascriptissexy.com
'개발 > JavaScript' 카테고리의 다른 글
[Vanilla Javascript] Jasmine을 이용해 테스트 코드 작성해보기 -2 (0) | 2022.04.10 |
---|---|
[Vanilla Javascript] Jasmine을 이용해 테스트 코드 작성해보기 -1 (0) | 2022.04.10 |
[Javascript] module export, import, 모듈 이름 수정 (0) | 2022.03.25 |
[Javascript] IIFE (즉시 실행 함수) (0) | 2022.03.25 |
[Javascript] Strict mode (0) | 2022.03.25 |