카테고리 없음

날것 - 클로저의 활용

전낙타 2026. 8. 18. 20:21

클로저의 활용

전역 변수로 카운터 만들기의 문제

전역 변수는 누구나 접근할 수 있음으로 이건 당연히 문제가 발생할 확률이 높다

let num = 0

const increase = function () {
	return ++num;
}

console.log(increase()) // 1
console.log(increase()) // 2
console.log(increase()) // 3

그렇다고 함수 내부에 값을 선언해놓으면 그 값이 유지되지 않는다.

const increase = function () {
	let num = 0
	return ++num;
}

console.log(increase()) // 1
console.log(increase()) // 1
console.log(increase()) // 1

여기까진 뽀삐도 설명할 수 있다.
그렇다면 어떤 방법이 있을까?

정답은 클로저에 있다

const increase = (function () {
	let num = 0
	return function() {
		return ++num;
	};
}());

console.log(increase()) // 1
console.log(increase()) // 2
console.log(increase()) // 3

이럼 클로저때문에 실행 함수는 호출된 이후 소멸하지만, 즉시 실행 함수가 반환한 클로저는 increase 변수에 할당되어 num 값에 접근할 수 있다

즉 클로저를 사용해서 private 상태를 만들어버린것 ㅋㅋ

이걸 생성자 함수를 사용해 좀 더 가독성 좋게 표현할 수 있음

const Counter = (function() {
	let num = 0
	
	function Counter() {
		// this.num = 0; 프로퍼티는 public하므로 은닉되지 않는다. 외부에서도 접근 가능함
	}
	
	Counter.prototype.increase = function() {
		return ++num;
	};
	
	Counter.prototype.decrease = function() {
		return num > 0 ? --num : 0;
	};
	
	return Counter;
}())

const couter = new Counter();

console.log(counter.increase()) // 1
console.log(counter.increase()) // 2

console.log(counter.decrease()) // 1
console.log(counter.decrease()) // 0

약간의 꼼수를 확인할 수 있는게 프로퍼티는 외부에서 접근할 수 있지만 실행 함수 내에서 선언된 num 변수는 접근 못하니깐 이렇게 설정한 모습

함수형 프로그래밍 스타일 — 고차 함수로 상태 유지

{makeCounter처럼 보조 함수를 인자로 받고, 상태를 클로저에 가둔 함수를 반환}

function makeCounter(aux) {
	let counter = 0;
	
	return function() {
		counter = aux(counter);
		return counter;
	}
}

function increase (n) {
	return ++n;
};

function decrease (n) {
	return --n;
};

const increaser = makeCounter(increase);
console.log(increaser()); // 1
console.log(increaser()); // 2

const decreaser = makeCounter(decreaser);
console.log(decreaser()); // -1
console.log(decreaser()); // -2

레전드레전드 ㅋㅋ
함수를 인자로 전달받는 고차함수 + 클로저를 사용해서 counter의 값을 변경할 수 있음

하나 특이한건 makeCounter가 다시 한번 실행 컨텍스트에 올라가버리면 새로운 makeCounter의 새로운 렉시컬 환경이 생기고 decreaser는 해당 렉시컬 환경을 바라보게 됨으로 increaser가 바라보던 렉시컬 환경과는 별개의 환경이 생긴다

그렇다면 이 방식으로 같은 렉시컬 환경과 묶어버릴 수 있음

const counter = (function (aux) {
	let counter = 0;
	
	return function(aux) {
		counter = aux(counter);
		return counter;
	};
}());

function increase (n) {
	return ++n;
};

function decrease (n) {
	return --n;
};

console.log(counter(increase)); // 1
console.log(counter(increase)); // 2

console.log(counter(decreaser)); // 1
console.log(counter(decreaser)); // 0

재밌네
재뮛서

캡슐화와 정보 은닉

자바스크립트는 접근 제한자가 없다(레전드 ㅋㅋ)
그래서 클로저로 흉내낸다 - 생성자 함수 + 즉시 실행 함수 조합

function Person(name, age) {
	this.name = name; // public
	let _age = age; // private
	
	this.sayHi = function() {
		console.log(`${this.name} , ${_age}`)
	}
}

const me = new Person('Lee', 20);
me.sayHi(); // Lee , 20
console.log(me.name) // Lee
console.log(me._age) // undefined

이건 인스턴스 메서드라서 Person 객체가 생성될떄마다 중복 생성됨
이를 prototype으로 예방할 수 있음

const Person = (function() {
	let _age = 0;
	
	function Person(name, age) {
		this.name = name; // public
		_age = age; // private
	}
	
	Person.prototype.sayHi = function() {
		console.log(`${this.name} , ${_age}`)
	}
}

const me = new Person('Lee', 20);
me.sayHi(); // Lee , 20
console.log(me.name) // Lee
console.log(me._age) // undefined

근데 이것도 문제가 있음...

const me = new Person('Lee', 20);
me.sayHi(); // Lee , 20

const you = new Person('Kim', 30);
you.sayHi(); // Kim , 30

me.sayHi(); // Lee, 30

이는 Person.prototype.sayHi 메서드가 단 한번 생성되는 클로저이기 떄문에 발생하는 현상임
얜 잘못 만들어진게 맞다

참고 자료

반응형