클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다. 반환된 내부함수가 자신이 선언됐을 때 어휘적 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경 밖에서 호출되어도 그 환경에 접근할 수 있는 함수를 말한다.
즉 함수가 생성될 당시 외부 변수를 기억하고, 생성 이후에도 계속해서 접근할 수 있는 구조를 클로저라고 한다.
클로저를 이해하기 앞서 흔히 말하는 렉시컬 환경에 대해 이해가 필요하다.
렉시컬 환경은 코드가 실행되는 특정 컨텍스트에 대한 식별자(변수, 함수 등)의 정보를 담고 있는 공간이다.
렉시컬 환경이 생성되는 시점
1. 전역 코드가 실행될 때 : 전역 코드가 시작되면, 전역 렉시컬 환경이 생성됩니다. 이 환경은 전역 변수와 함수를 포함하며, 외부 참조는 null
2. 함수가 호출될 때 : 함수가 호출되면, 새로운 렉시컬 환경이 만들어집니다. 이 환경은 함수의 매개변수와 지역 변수를 포함하며, 외부 참조는 해당 함수가 선언된 위치의 렉시컬 환경
3. 블록 스코프가 생성될 때 : let과 const로 선언된 변수는 그들이 속한 블록 스코프에서만 유효합니다. 따라서 중괄호 {}로 구분된 각각의 블록 스코프마다 새로운 렉시컬 환경이 만들어진다.
function makeAdder(x) {
return function(y) {
return x + y;
}
}
const add = makeAdder(3);
console.log(add(2));
먼저 렉시컬 환경이 어떻게 생성되는지 보면 코드 실행 시 전역 Lexical이 생성된다.
전역 Lexical 환경 | makeAdder Lexical 환경 | add Lexical 환경 |
---|---|---|
makeAdder : function | ||
add : function |
makeAdder(3); 함수를 호출하게 되면 makeAdder 렉시컬 환경이 생성되고 makeAdder 함수는 전역 렉시컬 환경을 참조하게 된다.
전역 Lexical 환경 | makeAdder Lexical 환경 | add Lexical 환경 |
---|---|---|
makeAdder : function | x : 3 | |
add : function |
add(2); 함수를 호출하게 되고 add 렉시컬 환경이 생성된다. add 함수는 makeAdder함수 내부에 선언되고 있다 그래서 add 렉시컬 환경은 makeAdder 렉시컬 환경을 참조하게 된다.
전역 Lexical 환경 | makeAdder Lexical 환경 | add Lexical 환경 |
---|---|---|
makeAdder : function | x : 3 | y : 2 |
add : function |
내부 렉시컬 환경은 외부 렉시컬 환경에 대한 참조를 받는다.
변수를 찾을 때 내부에서 찾고 없으면 외부에서 찾고 거기에도 없으면 전역 렉시컬 까지 범위를 넓혀서 찾는다.
add(2) 함수를 호출할때 x + y를 실행해야한다. 어떻게 동작되는걸까?
add 함수가 생성된 이후에도 상위 함수가 소멸되지 않고 makeAdder의 x에 접근이 가능한 것을 함수와 렉시컬 환경의 조합 즉 클로저라고 한다.
function makeAdder(x) {
return function(y) {
return x + y;
}
}
const add3 = makeAdder(3);
console.log(add3(2)); // 5
const add10 = makeAdder(10);
console.log(add10(3)) // 13
add3 과 add10은 반환하는 결과 값이 다르다. 이유는 각각 서로 다른 렉시컬 환경을 가지고 있기 때문에 렉시컬 환경에 저장된 변수의 값이 다르기 때문이다.
그럼 만약 x값을 수정할 수 있을까? 정답은 불가능 하다 오직 add의 익명 함수로만 x에 접근하여 값을 더할 수 있다. 이는 x 값의 은닉화를 성공한 것이다.