자바스크립트는 어휘적 스코프(Lexical Environment)를 사용한다. 어휘적 스코프란 함수가 호출 시저의 스코프가 아니라 지산이 정의된 시점의 변수 스코프를 사용하여 실행된다는 뜻이다.
이를 구현하기 위해서는 함수 객체의 내부 상태에 함수의 코드 뿐만 아니라 함수가 정의된 스코프에 대한 참조도 반드시 포함되어 있어야 한다. 이렇게 함수 객체와 스코프를 조합한 것을 클로저(Closure)라고 부른다.
클로저가 우용할 때는 함수가 정의된 곳과 다른 스코프에서 호출될 때 뿐이다. 가장 흔한 경우는 함수가 함수를 정의해 반환하는 경우이다.
let one;
one = 1;
function addOne(num) {
console.log(one + num);
}
addOne(5);
코드가 실행되면 스크립트 내에서 선언한 변수(one, addOne)들이 렉시컬환경에 올라간다. let으로 선언된 변수는 렉시컬 환경에 올라가지만 초기화 되진 않는다. 따라서 사용은 못한다. 그에 비해 함수 선언문은 변수와 달리 바로 초기화가 된다. 따라서 사용이 가능하다.
전역 Lexical 환경
one: 1
addOne: function
let one은 아직 할당이 안되어 있기 때문에 초기값 undefined를 갖는다. 이제 사용을 해도 에러가 발생하지 않는다. 이제 one 변수에 숫자 1이 할당 되었다. 함수 선언은 초기에 이미 완료 되었고, 마지막 라인(addOne(5))로 가서 함수가 실행된다. 그 순간 새로운 렉시컬 환경이 만들어진다. 이곳에는 함수가 넘겨받은 매개변수와 지역 변수들이 저장된다.
내부 Lexical 환경
num: 5
함수가 호출되는 동안 함수에서 만들어진 내부 렉시컬 환경과 외부에서 받은 전역 렉시컬 환경 두개가 만들어 진다. 내부 렉시컬 환경은 전역 렉시컬 환경에 대한 참조를 갖는다.
addOne 함수 호출시 one과 num을 찾는데 우선 내부렉시컬 환경에서 찾고, 이곳에도없으면 외부에서 찾는다. 내부에 num이 있고, one은 없으니 외부로 가서 one을 찾아 두 값을 더해주게 될수 있다.
function makeAdder(x) {
return function (y) {
return x+y;
}
}
const add3 = makeAdder(3);
console.log*(add3(2)); // 5
const add10 = makeAdder(10);
console.log(add(5)); // 15
console.log(add(1)); // 4
makeAdder의 내수 함수는 자신이 y를 가지고 있고, 상위 함수인 makeAdd의 매개변수x에 접근 할수 있다. add3 함수가 선언된 이후에도 변화 없이 상위 함수로 호출 할때 사용했던 인수에 접근이 가능하다.
이러한 클로저는 함수와 그 함수에 렉시컬 환경 등의 조합이다. 함수가 생성될 당시 외부 변수를 기억하고 생성된 이후에도 그 변수에 계속 접근이 가능한 기능이다.
외부 함수의 실행이 끝나서 외부 함수가 소멸된 이후에도 내부 함수가 외부 함수의 변수에 접근 할수 있다.
makeAdder(10)이 호출되지만 add3에는 영향을 주지 않는다. add10과 add3은 서로 다흔 환경을 가지고 있는 것이다.
let scope = 'global'; // 전역 변수
function checkScope() {
let scope = 'local; // 로컬 변수
funcion f() { return scope; } // 이 스코프에 있는 값을 반환한다.
return f();
};
let s = checkScope()();
console.log(s);
자바스크립트 함수는 자신이 정의된 스코프에서 실행이 된다. 중첩된 함수 f()는 변수 scope가 local이었던 스코프에서 정의되었다. 이것은 f를 어디에서 실행하든 상관없이 계속 유지된다. 따라서 변수 s는 'local'을 반환한다. 클로저는 자신을 정의한 외부 함수의 로컬 변수와 매개변수를 그대로 캡처한다.