.png)
클로저란?
- 어떤 함수가 리턴하는 값이 함수 일 때, 외부함수의 변수에 접근 할 수 있는 내부함수를
클로저라고 한다.
→ 외변접내!
- 내부함수는 외부함수의 렉시컬 환경을 저장하여, 변수를 참조한다. (가지고 있는다.)
- 여기서
렉시컬 환경이란 아주 간단하게, 함수 실행될 때, 함수가 가진 변수의 정보를 저장하는 일종의 배열이라고 볼 수 있다.
function outer (a){
return function inner (b) {
return a + b
}
}
let outer5 = outer(5)
outer5(10)
클로저 내부에서 매개변수
- 내부 함수를 포함한 외부 함수는 내부함수의 매개변수에 접근이 불가능 하지만, 내부함수는 외부함수의 매개변수에 접근이 가능하다.
- 이는 매개변수도 일종의 함수 스코프를 가진 변수이기 때문으로, 내부 함수의 매개변수는 자신이 속한 내부함수의 스코프 밖에서 접근 할 수 없기 때문.
클로저 활용하기
데이터 보존
- 클로저의 특징 중 하나는 데이터를 보존한다는 것이다.
- 일반 함수는 함수의 실행이 종료된 경우 함수 내부의 변수를 사용 할 수 없다.
- 하지만,
클로저의 경우 외부함수의 변수를 그대로 사용 할 수 있는데, 이유는 아래와 같다.
- JS는 함수가 실행 될 때,
렉시컬 환경에 당시의 변수와 값을 저장한다.
- 외부함수가 실행이 종료되고 생명주기가 끝난다고 해도, 외부함수의 렉시컬 환경을 삭제하지는 않는다.
- 이로 인해 내부함수는 렉시컬 환경에 저장된 외부함수의 변수의 값을 참조 할 수 있게 된다.
(이를 외부 렉시컬 환경을 참조한다고 한다.)
- 즉, 함수를 실행 할 때 해당 함수의 현재 변수들을 렉시컬 환경에 저장하고, 해당 값을 계속 가지고 있는다는 것.
- 외부함수는 종료됐지만, 내부의 함수가 아직 실행되지 않았으므로, 내부함수가 참조해야할 값을 저장하고 있다고 생각 해 볼 수 있을 것 같다.
function outer (a){
return function inner (b) {
return a + b
}
}
let outer5 = outer(5)
outer5(5)
outer5(10)
outer5(50)
- 또한 하나의 함수가 서로 다른 변수명에 지정될 때, 내부의 변수의 값을 서로 공유하지 않는다.
- 이는 아래의 예시에서
fn1 과 fn2 라는 변수에 outer 함수가 할당되고, 실행 될 때 서로 다른 렉시컬 환경에 변수에 대한 정보를 저장하고, 참조하기 때문이다.
→ 이때 저장되는 렉시컬 환경은 리턴되는 함수인 inner에 대한 정보이며, 필요한 변수인 a는 상위 렉시컬 환경에서 참조하여 사용하게 된다.
- 즉,
fn1 의 렉시컬 환경, fn2 의 렉시컬 환경이 각각 생성되어 저장된다.
function outer() {
let a = 0
return function inner(){
console.log(a++)
}
}
let fn1 = outer()
let fn2 = outer()
fn1()
fn1()
fn1()
fn2()
fn2()
fn1, fn2 에 함수를 할당하고, 각각의 함수를 실행시키면, 각각 서로 다른 렉시컬 환경이 생성되며, 이 때 렉시컬 환경은 반환받는 함수인 inner() 를 기준으로 생성된다.
(서로 다른 공간을 가진다.)
- 함수를 실행할 때 마다,
a++ 가 실행되면, 자신의 렉시컬 환경에서 a를 찾고, 없는 경우 외부 렉시컬 환경(outer())에서 참조할 변수 a를 찾게 된다.
- 외부 렉시컬 환경에서 찾은 변수
a (a = 0)를 참조하고, a++ 가 실행되면서 외부 렉시컬 환경에 저장된 변수의 값을 갱신시킨다.
- 위의 과정을 거쳐
fn1, fn2 를 실행할 때 마다 서로 간섭하지 않고 각각의 함수 실행 결과에 1이 추가된다.
(각각 새로운 렉시컬 환경이 생성되므로, 서로 다른 공간에 각각의 정보를 갱신시키는 것이기 때문.)
- 이렇게 서로의 정보에 영향을 주지않는 작성방식을
모듈화라고 한다.
정보의 접근 제한(은닉화, 캡슐화)
- 클로저의 또 하나의 특징은 정보를
은닉화 할 수 있다는 점이다.
은닉화 란 내부의 변수의 값을 외부에서 변경 할 수 없는 것을 이야기한다.
- 객체나 일반 변수를 이용하면, 외부에서 쉽게 값을 바꿀 수 있다.(재할당)
- 그러나 클로저를 이용하면 함수 내부의 변수에 접근 할 수 없는 환경을 만들어 줄 수 있다.
function outer () {
let result = 0
return function sum() {
return result = result + 1
}
}
let counter = outer()
counter()
counter()
counter()
- 해당 환경에서는 이미 지정된 클로저 함수를 실행하는 것으로만 해당 변수의 값을 변경 시켜 줄 수 있다.
- 변수
result 를 코드를 직접 바꾸지 않고, 외부에서 변경하는 것은 불가능하다.
→ 이는 스코프 규칙에 따라 외부에서 내부 변수에 접근하는 것이 불가능 하기 때문이다.
클로저 모듈 패턴
- 클로저 내부에 하나의 함수가 아니라, 객체를 이용하여 서로의 기능에 영향을 주지않는 다수의 함수를 작성 할 수 있다.
- 아래처럼 작성하는 경우, 내부의 각가의 함수들이 서로에게 영향을 주지않고 작동하는데, 이를
클로저 모듈 패턴라고 한다.
function outer () {
let result = 0
return {
sum : () => {
return result = result + 1
}, minus : () => {
return result = result - 1
}
}
}
let counter = outer()
counter.sum()
counter.sum()
counter.minus()
+
- 함수가 실행 될 때마다 렉시컬 환경이 생성되고, 함수 내부의 변수들의 최초의 값을 저장한다.
- 함수가 실행될 때 만약 참조할 변수의 값이 자신의 렉시컬 환경에 없는 경우, 외부 렉시컬 환경을 참조한다.
(외부 렉시컬 환경은 나의 상위 스코프의 렉시컬 환경을 이야기 한다.)
- 렉시컬 환경이 함수의 값을 저장하고 있기 때문에 클로저 함수가 동작 할 수 있는 것.
(아니라면, 클로저가 참조 할 수 있는 값이 없을 것이다.)