ES5의 스코프는 함수 레벨 스코프까지 지원 했었는데 ES6부터는 블록 레벨의 스코프를 지원해 let, const키워드를 통해 블록 레벨 스코프의 사용이 가능해졌다.
// ES5
function apple() {
if(ture) {
var color = 'red';
}
console.log(color); //red
}
// ES6
function apple() {
if(ture) {
let color = 'red';
console.log(color); //red
}
console.log(color); //not defined
}
예제를 보면 ES5의 var
키워드는 함수 레벨 스코프를 갖고 ES6의 let
키워드는 블록 레벨 스코프를 가지고 있다.
스코프는 함수를 호출할 때가 아니라 선언할 때 생긴다. 함수를 어디에서 호출하였는지는 스코프 결정에 아무런 의미를 주지 않는다.
var name = 'aehee';
function a(name) {
console.log(name);
}
function b() {
a(name);
}
function c() {
var name = 'autumn';
a(name);
}
b(); // aehee
c(); // autumn
해당 코드를 보면 쉽게 이해할 수 있다.
변수를 선언하고 포기화 했을 때 선언 부분이 최상단으로 끌어올려지는 현상을 호이스팅이라고 한다. 함수 표현 식이 아닌 함수 선언식일 때는 식이 통째로 끌어올려진다.
isName();
isFood();
const isName = function(){
console.log('aehee');
}
function isFood(){
console.log('coffee');
}
위 코드를 보면 isName은 Context에 아직 대입되기 전이어서 에러가 발생한다.
클로저는 반환된 내부 함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(Scope) 밖에서도 호출되어도 그 환경(Scope)에 접근할 수 있는 함수를 말한다.
쉽게 말하자면! 내부 함수는 외부함수의 지역변수에 접근할 수 있는데 외부함수의 실행이 끝나서 외부함수가 소멸한 이후에도 내부 함수가 외부함수의 변수에 접근할 수 있다
클로저의 핵심은 스코프를 이용해서 변수의 접근 범위를 닫는(폐쇄) 것에 있다.
클로저가 가장 유용하게 사용되는 상황은 현재 상태를 기억하고 변견된 최신 상태를 유지하는 것이다.
기본적으로 잘 쓰이는 예제를 가져왔다.
클릭을 할 때 마다 count가 증가하는 함수가 있다고 가정해보자.
const addButton = document.querySelector('button');
addButton.addEventListener('click', handleClick);
let count = 0;
function handleCilck() {
count++
return count;
}
위 같은 경우에는 count를 전역변수로 사용해줘야 count가 증가한다.
이럴경우 클로저를 사용할 수 있다.
const addButton = document.querySelector('button');
addButton.addEventListener('click', handleClick());
function handleCilck() {
let count = 0;
return function () {
count++
return count;
}
}
이렇게 작성해주면 handleClick
의 Lexical environment를 참조하는 함수를 addButton의 콜백함수로 이용해 전역객체 없이 구현할 수 있다.
function outer() {
let name = 'lee';
if (true) {
let city = 'seoul';
return function inner() {
console.log(city);
};
}
}
여기서 객체를 확인해보면 city는 클로저가 아니다.
클로저는 내부에 선언된 함수가 외부함수의 지역변수를 사용해 줬을 때만 클로저라고 한다.
inner 함수에도 클로저를 사용하고 싶으면 name 변수를 사용해줘야 한다.
function outer() {
let name = 'lee';
if (true) {
let city = 'seoul';
return function inner() {
console.log(name);
};
}
}
outer의 객체를 확인 했을 때 closure에 name이 들어온다.