중첩된 함수 그룹에서 내부 함수가 상위 범위의 변수 및 기타 리소스에 액세스 할 수 있음을 의미.
즉, 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정한다는 뜻이며, 가장 중요한 점은 함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 결정된다.
var x = 1;
function foo() {
var x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // 1
bar(); // 1
클로저는 함수가 어디에서 선언되었는가에 따라 lexical scope가 달라진다. (호출 아니고 선언!!!!!!!!)
foo() 함수
와 bar() 함수
모두 전역 범위에서 선언됨
즉, foo() 함수
와 bar() 함수
의 lexical scope 는 전역 스코프
bar() 함수는 전역 범위에서 선언되었고, 그에 따른 렉시컬 스코프는 전역 스코프이다.
따라서 bar() 함수 내부의 console.log(x)
에서 x는 전역 범위에서 선언된 x = 1
을 참조한다.
foo() 함수 내부에서 x를 선언하긴 했지만, bar();
는 foo() 함수 내부에서 선언된 것이 아니라 호출된 것이기 때문에 실제로 선언된 위치(전역)을 기준으로 x = 1
을 참조한다.
var x = 1;
function foo() {
var x = 10;
//bar() 함수 선언!
function bar() {
console.log(x);
}
bar();
}
foo(); // 10
foo() 함수 내부에서 선언된 bar() 함수의 lexical scope는 foo() 함수이므로 foo() 함수의 변수에 접근할 수 있다.
따라서 bar() 함수 내부의 console.log(x)
에서 x는 foo() 함수 범위에서 선언된 x = 10
을 참조한다.
function createCount() {
let a = 0;
return function () {
return a += 1
}
}
const count = createCount();
//const count = function () { return a += 1 }
console.log(count()); //1
console.log(count()); //2
console.log(count()); //3
count 함수를 클로저라고 할 수 있다.
별도의 상태 관리를 하지 않아도, 함수 내부에서 누적될 수 있는 특정한 변수를 사용할 수 있고, 이때 클로저라는 개념을 유용하게 사용할 수 있다.
const h1El = document.querySelector('h1')
const h2El = document.querySelector('h2')
let h1IsRed = false;
let h2IsRed = false;
h1El.addEventListener('click', event => {
h1IsRed = !h1IsRed;
h1El.style.color = h1IsRed ? 'red' : 'black';
})
h2El.addEventListener('click', event => {
h2IsRed = !h2IsRed;
h2El.style.color = h2IsRed ? 'red' : 'black';
})
const createToggleHandler = () => {
let isRed = false;
return event => {
isRed = !isRed;
event.target.style.color = isRed ? 'red' : 'black';
}
}
h1El.addEventListener('click', createToggleHandler())
h2El.addEventListener('click', createToggleHandler())