[JavaScript]
기초
- [JS] Js기초
- [JS] Js기초 변수
- [JS] Js기초 자료형
- [JS] Js기초 형변환
- [JS] Js기초 연산자
- [JS] Js기초 반복문
- [JS] Js기초 switch
- [JS] Js기초 function
- [JS] Js기초 객체
- [JS] Js기초 배열
중급
- [JS] Js중급 호이스팅(Hoisting)과 TDZ(Temporal Dead Zone)
- [JS] Js중급 생성자함수
- [JS] 객체 메소드(Object methods), 계산된 프로퍼티(Computed property)
- [JS] 심볼(Symbol)
- [JS] WeakMap WeakSet
- [JS] 숫자, 수학 method (Number, Math)
- [JS] 문자열 메소드(String methods)
- [JS] 배열 메소드(Array methods)
- [JS] 구조 분해 할당 (Destructuring assignment)
- [JS] 매개변수 리스트와 전개 문법(Rest parameters and spread syntax)
- [JS] 클로저(Closure)
- [JS] setTimeout / setInterval
- [JS] call / apply / bind
- [JS] 상속, 프로토타입(Prototype)
- [JS] 클래스(Class)
- [JS] 클로미스(Promise)
- [JS] 비동기 처리(Async/Await)
- [JS] Generator
- [JS] 메모리 릭(Memory Leak)
클로저! 함수와 그 함수가 선언된 렉시컬 환경의 조합이야! 🎯
1. 클로저 기본 개념
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
| // 기본적인 클로저
function createCounter() {
let count = 0; // 외부에서 직접 접근 불가능한 변수
return {
increase() {
return ++count;
},
decrease() {
return --count;
},
getCount() {
return count;
},
};
}
const counter = createCounter();
console.log(counter.increase()); // 1
console.log(counter.increase()); // 2
console.log(counter.decrease()); // 1
console.log(counter.getCount()); // 1
// count 변수에 직접 접근 불가
console.log(counter.count); // undefined
|
2. 클로저를 활용한 프라이빗 변수
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
| function createWallet(initialBalance) {
let balance = initialBalance; // 프라이빗 변수
return {
deposit(amount) {
if (amount > 0) {
balance += amount;
return `${amount}원이 입금되었습니다. 잔액: ${balance}원`;
}
return "유효하지 않은 입금액입니다.";
},
withdraw(amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
return `${amount}원이 출금되었습니다. 잔액: ${balance}원`;
}
return "출금이 불가능합니다.";
},
getBalance() {
return `현재 잔액: ${balance}원`;
},
};
}
const myWallet = createWallet(1000);
console.log(myWallet.deposit(500)); // "500원이 입금되었습니다. 잔액: 1500원"
console.log(myWallet.withdraw(200)); // "200원이 출금되었습니다. 잔액: 1300원"
console.log(myWallet.getBalance()); // "현재 잔액: 1300원"
|
3. 클로저를 이용한 모듈 패턴
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
31
32
| const userModule = (function () {
// 프라이빗 변수
let users = [];
// 프라이빗 함수
function validateUser(user) {
return user.name && user.age;
}
// 퍼블릭 API
return {
addUser(user) {
if (validateUser(user)) {
users.push(user);
return "사용자가 추가되었습니다.";
}
return "유효하지 않은 사용자입니다.";
},
getUsers() {
return [...users]; // 배열의 복사본 반환
},
findUser(name) {
return users.find((user) => user.name === name);
},
};
})();
userModule.addUser({ name: "김코딩", age: 20 });
console.log(userModule.getUsers()); // [{name: "김코딩", age: 20}]
console.log(userModule.users); // undefined (프라이빗)
|
4. 클로저와 커링
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
| function multiply(a) {
return function (b) {
return function (c) {
return a * b * c;
};
};
}
// 커링된 함수 사용
const mult2 = multiply(2); // a = 2로 고정
const mult2_3 = mult2(3); // b = 3으로 고정
console.log(mult2_3(4)); // 2 * 3 * 4 = 24
// 한 번에 호출
console.log(multiply(2)(3)(4)); // 24
// 실용적인 예제
function formatPrice(currency) {
return function (price) {
return `${currency}${price.toFixed(2)}`;
};
}
const formatUSD = formatPrice("$");
const formatKRW = formatPrice("₩");
console.log(formatUSD(123.456)); // "$123.46"
console.log(formatKRW(1000)); // "₩1000.00"
|
5. 클로저와 이벤트 핸들러
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| function createButtonCounter() {
let count = 0;
function updateDisplay() {
console.log(`클릭 횟수: ${count}`);
}
return function handleClick() {
count++;
updateDisplay();
};
}
// DOM 이벤트 리스너에서 사용
const button = document.createElement("button");
button.textContent = "클릭하세요";
button.addEventListener("click", createButtonCounter());
|
6. 클로저를 활용한 메모이제이션
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
| function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log("캐시된 결과 사용");
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
console.log("새로운 결과 계산");
return result;
};
}
// 피보나치 수열 계산
const fibonacci = memoize(function (n) {
if (n < 2) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
console.log(fibonacci(10)); // 처음 계산
console.log(fibonacci(10)); // 캐시된 결과
|
꿀팁! 🍯
- 메모리 관리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // BAD: 메모리 누수 가능성
function createBadClosure() {
const largeData = new Array(1000000);
return function () {
console.log(largeData.length);
};
}
// GOOD: 필요한 데이터만 유지
function createGoodClosure() {
const largeData = new Array(1000000);
const dataLength = largeData.length;
return function () {
console.log(dataLength);
};
}
|
- 성능 최적화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // 클로저 재사용
const createLogger = (prefix) => {
// 클로저는 한 번만 생성
const logger = (message) => {
console.log(`${prefix}: ${message}`);
};
return logger;
};
const debugLog = createLogger("DEBUG");
const errorLog = createLogger("ERROR");
debugLog("테스트 메시지"); // "DEBUG: 테스트 메시지"
errorLog("오류 발생!"); // "ERROR: 오류 발생!"
|
이제 클로저는 마스터! 😎
프라이빗 변수 관리와 상태 유지가 필요한 상황에서 유용하게 사용할 수 있어!