"변수에 접근할 수 있는 범위"를 뜻한다.
자바스크립트에서 변수는 정의된 위치에 따라 접근 가능한 범위가 결정된다.
크게 보면 아래 두 가지가 있다.
let a = 10; // 전역 스코프
function printA() {
let b = 20; // 지역 스코프
console.log(a); // ✅ 가능
console.log(b); // ✅ 가능
}
printA();
console.log(b); // ❌ ReferenceError
자바스크립트에서는 var 키워드로 선언한 변수는 함수 단위로 스코프를 가진다.
function test() {
var x = 100;
}
console.log(x); // ❌ ReferenceError
let과 const는 블록({}) 단위로 스코프를 가진다.
{
let foo = 'bar';
}
console.log(foo); // ❌ ReferenceError
var 대신 항상 let이나 const를 사용하는 게 좋다.JS는 렉시컬 스코프를 따르는 언어다.
"렉시컬"이라는 말은 코드가 적힌 위치를 기준으로 스코프가 결정된다는 뜻이다.
function outer() {
let x = 10;
function inner() {
console.log(x);
}
inner();
}
outer();
위 코드에서 inner() 함수는 자신이 정의된 위치 기준으로 x를 찾게 된다.
"실행되는 위치"가 아니라, "정의된 위치"를 기준으로 상위 스코프를 찾는 것이 렉시컬 스코프이다.
클로저는 "외부 함수의 변수에 접근할 수 있는 내부 함수"이다.
좀 더 정확히 말하면, 외부 함수가 종료된 이후에도 그 함수의 변수에 접근할 수 있는 함수가 클로저이다.
function makeCounter() {
let count = 0;
return function () {
count += 1;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
여기서 내부 함수는 count 변수에 계속 접근하고 있다.
makeCounter()는 이미 실행이 끝난 함수임에도 불구하고 count는 살아있다.
👉 이게 바로 클로저!
JS에서는 함수가 실행된 이후에도 그 함수가 참조하고 있는 변수들을 메모리에 유지하기 때문에 클로저가 외부 변수에 접근할 수 있는 것!
메모리 누수 가능성
의도치 않은 참조 유지
예시
const functions = [];
for (var i = 0; i < 3; i++) {
functions.push(() => console.log(i));
}
functions[0](); // 3
functions[1](); // 3
functions[2](); // 3
var를 쓰면 i는 공유되기 때문에 예상치 못한 결과가 나온다.let을 쓰거나, IIFE로 감싸서 클로저를 잘라줘야 한다.