[JavaScript] 클로저
Subject
JavaScript
2021.01.19
정의
- 클로저는 함수와 함수가 선언된 어휘적 환경의 조합.
- 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야.
어휘적 범위 지정(Lexical scoping)
function init() {
var name = 'Mozilla'; // name은 init에 의해 생성된 지역 변수이다.
function printName() {
// printName() 은 내부 함수이며, 클로저다.
console.log(name); // 부모 함수에서 선언된 변수(name)를 사용한다.
// + 만약 printName()가 자신만의 name변수를 가지고 있었다면, name대신 this.name을 사용했을 것
}
printName();
}
init();
- 위 코드를 실행하면 printName() 함수 내의 console.log()문이 부모 함수에서 정의한 변수 name의 값을 성공적으로 출력함.
- 이 예시를 통해 함수가 중첩된 상황에서 파서가 어떻게 변수를 처리하는지 알 수 있다.
이는 어휘적 범위 지정(lexical scoping)의 한 예다. - lexical ?
어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미 - 중첩된 함수는 외부 범위(scope)에서 선언한 변수에도 접근할 수 있다.
클로저(Closure)
예시 1
function makeFunc() {
var name = 'Mozilla';
function printName() {
console.log(name);
}
return printName;
}
var myFunc = makeFunc(); // myFunc변수에 printName 리턴함
// 유효범위의 어휘적 환경을 유지
myFunc(); // 리턴된 printName 함수를 실행 (name 변수에 접근)
- 몇몇 프로그래밍 언어에서, 함수 안의 지역 변수들은 그 함수가 처리되는 동안에만 존재.
makeFunc() 실행이 끝나면 (printName 리턴되고 나면)
name 변수에 더 이상 접근할 수 없게 될 것으로 예상하는 것이 일반적. - 자바스크립트의 경우엔 다름.
- 자바스크립트는 함수를 리턴하고, 리턴하는 함수가 클로저를 형성하기 때문.
- 클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다.
- 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.
예시 2
function makeAdder(x) {
var y = 1;
return function (z) {
y = 100;
return x + y + z;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
//클로저에 x와 y의 환경이 저장됨
/*
▶ add5, add10 둘 다 리턴 시, makeAdder의 리턴 값은 함수. (return function (z) {})
- function (z) {} 안까지는 접근한게 아니라 보는게 맞을 듯.
- 매개변수 x 값은 저장되어 있는 상태겠지 add5, add10은.
*/
console.log(add5(2)); // 107 (x:5 + y:100 + z:2)
console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
//함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산
/*
▶ add5, add10를 실행하려면 function (z) {} 이니까,
z값을 인자로 보내주면, function (z) {} 안에 있는 리턴 값이 나올 것.
*/
- add5와 add10은 둘 다 클로저.
- 이들은 같은 함수 본문 정의를 공유하지만 서로 다른 맥락(어휘)적 환경을 저장한다.
- 함수 실행 시 add5의 맥락적 환경에서 클로저 내부의 x는 5 이지만 add10의 맥락적 환경에서 x는 10이다.
또한 리턴되는 함수에서 초기값이 1로 할당된 y에 접근하여 y값을 100으로 변경한 것을 볼 수 있다. (x값도 동일하게 변경 가능하다.) - 이는 클로저가 리턴된 후에도 외부함수의 변수들에 접근 가능하다는 것을 보여주는 포인트이며
클로저에 단순히 값 형태로 전달되는 것이 아니라는 것을 의미.
이게 맞는 걸까? 혼자 짜보는 코드
- 이 로직은 어떻게 돌아가는 걸까? (화살표 함수를 이용해서 한번 만들어봄)
// 이 로직은 이렇게 돌아가는 걸까?
const testFunc = (x) => (y) => (z) => x + y + z;
// |--1--|
// |--2--|
// |---------3---------|
let testX = testFunc(1); // - 위 1번 구간
console.log('testX: ', testX); // testX: [Function (anonymous)]
let testY = testX(2); // - 위 2번 구간
console.log('testY: ', testY); // testY: [Function (anonymous)]
let testZ = testY(3); // - 위 3번 구간
console.log('testZ: ', testZ); // testZ: 6
- 이렇게 이해한게 맞는건가. 잘모르겠다.
참고 자료