October 31, 2021
클로저
: 생명주기가 종료된 외부 함수의 식별자를 참조할 수 있는 중첩 함수로, 정보 은닉을 위해 사용합니다.커링
: 여러 인자를 받는 함수를 하나의 인자만 받는 함수들의 연결로 변환하는 방법으로, 함수의 재사용성을 높입니다.예시를 통해 클로저가 무엇인지와 클로저를 이용해서 정보를 은닉하는 방법을 확인해봅시다.
const getClosure = function() {
let num = 0
return function() {
return ++num
}
}
const increase = getClosure()
console.log(increase()) // 1
console.log(increase()) // 2
++num // ReferenceError: num is not defined
getClosure 함수를 호출하면 익명 함수를 반환하고 생명 주기를 마감(실행 컨텍스트 스택에서 pop)합니다. 외부 함수(getClosure)는 생명 주기를 마감했지만 중첩 함수(익명 함수)에서는 상위 스코프의 변수인 num을 참조하고 있습니다.
그러므로 이 중첩 함수는 클로저입니다.
외부에서 num 변수에 접근할 수 없습니다. (정보 은닉)
즉시 실행 함수를 사용하여 동일하게 작동하도록 구현할 수 있습니다.
const increase = (function() {
let num = 0
return function() {
return ++num
}
})()
console.log(increase()) // 1
console.log(increase()) // 2
++num // ReferenceError: num is not defined
커링은 클로저의 특징을 이용한 기법입니다.
예시를 통해 확인해봅시다.
const greetCurried = function(greeting) {
return function(name) {
console.log(greeting + ', ' + name)
}
}
const greetHello = greetCurried('Hello')
greetHello('seona') // Hello, seona
greetHello('minsung') // Hello, minsung
greetCurried('Hi there')('minsu') // Hi there, minsu
커링을 이용하면 이처럼 부분적으로 정의한 함수를 다시 정의해서 사용하는 것이 가능해 중복을 최소화할 수 있습니다.
함수 호출 시 위의 예시에서의 ‘Hello’처럼 매개 변수가 항상 비슷하다면 커링을 사용할 만한 후보라고 할 수 있습니다. 매개변수를 내부적으로 저장하여, 매번 인자를 전달하지 않아도 됩니다.
const greetDeeplyCurried = greeting => separator => emphasis => name =>
console.log(greeting + separator + name + emphasis)
const greetAwkwardly = greetDeeplyCurried('Hello')('...')('?')
greetAwkwardly('seona') // Hello...seona?
const sayHello = greetDeeplyCurried('Hello')(', ')
sayHello('.')('seona') // Hello, seona.
const askHello = sayHello('?')
askHello('seona') // Hello, seona?
화살표 함수를 사용하면 코드도 짧아지고, 가독성도 높일 수 있습니다.
클로저 사용 시에는 메모리 누수를 주의해야 한다. 클로저인 내부 함수가 참조하는 외부 함수의 식별자들을 더 이상 사용하지 않아도 가비지 콜렉터가 수집하지 못한다.
필요성이 사라진 시점에는 참조 카운트를 0으로 만들어 GC(가비지 컬렉터)가 수거해가도록 해서 메모리를 회수하여야 한다. 식별자에 null이나 undefined를 할당하는 방법으로 문제를 해결할 수 있다.
const outer = (function() {
let a = 1
const inner = function() {
return ++a
}
return inner
})()
console.log(outer())
console.log(outer())
outer = null // outer 식별자의 inner 함수 참조를 끊음