티스토리 뷰
▶️ 클로저란 무엇인가요?
클로저의 개념뿐 아니라, 클로저가 실제로 어떻게 사용될 수 있는지 구체적인 예시도 같이 설명해주세요. 그리고 클로저의 장단점에 대해서도 같이 작성해주세요.
클로저는 렉시컬 환경의 조합으로 스코프 체인을 통해 이뤄집니다.
전역 변수를 지역변수화 시켜 내부 함수에서만 참조할 수 있도록 해 전역 스코프로 인한 오류를 방지하는 기술로 데이터 은닉과 캡슐화를 사용할 수 있습니다.
클로저의 장점
- 데이터 은닉을 통해 외부 접근으로 인한 의도치 않은 변경을 막을 수 있습니다
- 함수 호출 이후에도 가비지 컬렉터에 의해 수집되지 않아 데이터의 상태를 기억시킬 수 있습니다
클로저의 단점
- 함수가 가비지 컬렉션에서 제외되어 메모리에 계속 남아있기 때문에 메모리 누수가 발생할 수 있습니다
클로저를 통해 할 수 있는 것
- 특정 계산을 위한 함수 또는 카운터를 위한 함수
- 데이터 은닉으로 계산식 또는 카운터에 의도치 않은 변경을 막을 수 있기 때문에 클로저를 사용하는 것이 유용합니다.
function closure() {
let cnt = 0;
// cnt 증가 함수
function cntPlus() {
console.log(cnt++);
}
// cnt에 인자를 전달해 값 변경하는 함수
function setCnt(val) {
console.log( cnt = val);
}
return { cntPlus, setCnt, printCnt };
}
const cntClosure = closure();
cntClosure.cntPlus();
cntClosure.setCnt(100);
- 객체 생성을 위한 사용
- printIntroduction함수로 반환된 createIntroduction의 매개변수는 가비지컬렉션에서 제외되기 때문에 상태를 유지할 수 있습니다 이로 인해 클래스와 생성자 함수 없이 간단하게 객체처럼 사용할 수 있습니다.
function createIntroduction(name,age) {
function printIntroduction(){
console.log(`제 이름은 ${name}입니다 ${age}살 입니다.`);
}
return printIntroduction
}
createIntroduction("김뽀삐",8)
▶️ 다음 코드를 읽고 결과를 설명해주세요.
var datepopCounter = (function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
},
};
})();
console.log(datepopCounter.value());
datepopCounter.increment();
datepopCounter.increment();
console.log(datepopCounter.value());
datepopCounter.decrement();
console.log(datepopCounter.value());
코드 해석
- datepopCounter 함수는 즉시 실행 함수로 정의와 동시 실행되는 함수입니다.
- datepopCounter 함수 내부에는 privateCounter 변수와 changeBy 함수가 있습니다.
- changeBy 함수는 privateCounter 변수의 값을 변경하는 함수입니다.
- datepopCounter 함수의 반환값은 객체 형태로 increment, decrement 메서드를 통해 changeBy 함수를 호출하고 privateCounter 변수 값을 조작합니다 value메서드로 privateCounter 변수를 반환합니다.
- 결론적으로 클로저를 이용해 외부에서 접근할수 없는 privateCounter 변수를 변경하고 상태를 저장하는 로직입니다.
결과
- 첫 번째 콘솔은 datepopCounter 함수의 privateCounter 변수를 출력하기 때문에 (0)이 출력됩니다
- 두 번째 콘솔은 datepopCounter.increment(); 이 두 번 호출되었기 때문에 1씩 증가하여 (2)가 출력됩니다
- 세 번째 콘솔은 위에서 +2를 해주었기 때문에 1을 뺀 (1)이 출력됩니다
▶️ 다음 코드를 읽고 결과를 설명해주세요.
코드에 문제가 있다면 어떻게 개선하면 좋을지 제안해주세요
function datepopPopdeal(amount) {
var defaultDisCountRate = 0.3
return {
price: function () {
return (1 - defaultDisCountRate) * amount;
}
}
}
let popdeal = datepopPopdeal(10000);
console.log("할인된 가격은 " + popdeal.price());
console.log("원래 가격은 " + popdeal.amount);
- popdeal 변수를 통해 datepopPopdeal 함수에 인자를 전달합니다
- 첫 번째 콘솔은 datepopPopdeal 함수의 반환 객체를 통해 amount으로 계산을 합니다**(1 - 0.3) * 10000 = 7000**
- 두 번째 콘솔은 amount값을 출력하고있지만 datepopPopdeal 함수의 반환 객체에 속하지않기 때문에 **undefined**으로 출력됩니다
개선된 코드
function datepopPopdeal(amount) {
var defaultDisCountRate = 0.3;
return {
price: function () {
return (1 - defaultDisCountRate) * amount;
},
print: amount,
};
}
let popdeal = datepopPopdeal(10000);
console.log("할인된 가격은 " + popdeal.price());
console.log("원래 가격은 " + popdeal.print);
▶️ 아래 코드를 실행할 경우, 발생할 수 있는 문제를 설명해주세요.
이를 해결하기 위한 방법도 제안해주세요. 추가로, 코드가 더 효율적으로 동작하도록 개선할 방법도 함께 설명해주세요.
function popUIButton(elements) {
for (let i = 0; i < elements.length; i++) {
elements[i].addEventListener("click", function() {
console.log(`지금 누른 버튼은 ${i}번째 입니다!`);
});
}
}
코드 해석
- popUIButton 함수는 버튼을 인자로 받고 있고 내부에 반복문이 있습니다 i의 최초값은 0이고, i는 elements길이보다 작고, i는 반복한다 는 조건을 가지고 있습니다.
- 각 버튼에 addEventListener로 클릭 이벤트를 주입하고 버튼을 클릭할 경우 해당 버튼의 인덱스를 출력하는 로직입니다
발생할 수 있는 문제
- 인자로 받고 있는 버튼이 배열 형태가 아니거나, 배열이 비어있는 경우 오류가 발생할 수 있습니다
- 버튼의 개수가 많을 경우, 모든 버튼에 addEventListener를 등록하면 성능 저하의 원인이 될 수 있습니다.
개선된 코드
document
.getElementById("container")
.addEventListener("click", function (event) {
if (event.target && event.target.matches(".button")) {
const btns = Array.from(document.querySelectorAll(".button")).indexOf(
event.target
);
console.log(`지금 누른 버튼은 ${btns}번째 입니다!`);
}
});
- 이벤트 델리게이션 패턴을 이용해 클릭한 대상에만 이벤트를 적용할 수 있도록 부모 요소에 addEventListener를 적용합니다
- 조건문을 이용해 이벤트 대상이 있고, 대상의 클래스명이 일치할 경우에만 접근이 가능합니다
- 동일한 클래스명을 가진 버튼 요소를 Array.from으로 배열로 변환하고 메서드 체이닝으로 indexOf를 이용해 클릭한 버튼의 인덱스를 추적합니다(querySelectorAll는 NodeList로 유사배열이지만 indexOf 사용을 위해 배열로 인식할 수 있도록 변환해줍니다.)
- 결국 btns 변수에는 클릭한 버튼의 인덱스가 할당됩니다.