해결된 질문
작성
·
552
·
수정됨
2
const test1 = () => {
let count = 0;
const inner = () => {
console.log(count); // closure
};
inner();
};
test1();
const test2 = (initialValue = 0) => {
let count = initialValue;
const inner = () => {
console.log(count); // block
};
inner();
};
test2();
test1의 inner에서는 count가 클로저 스코프를 갖는데, test2의 inner에서는 블록 스코프를 갖는 이유가 뭔가요?
답변 1
1
튜브님 안녕하세요! 질문 주셔서 감사합니다. 아주 중요한 내용을 담고 있는 질문이라고 생각합니다. count 변수가 기본값 매개변수를 사용할 때, 왜 다른 스코프에 위치하는지 알아볼게요.
기본값 매개변수는 ES6에 도입되었는데요 MDN의 문서에 따르면(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters) 아래와 같이 적혀 있습니다.
"The default parameter initializers live in their own scope, which is a parent of the scope created for the function body."
즉, 함수가 기본값 매개변수를 사용하면 기본값 매개변수 초기자는 함수 body 스코프의 부모가 되는 자체 스코프를 갖는다라고 합니다.
예를 들어,
const test1 = (value) => {
let count = value;
debugger;
}
test1(10);
위 test1 함수는 기본값 매개변수를 사용하지 않기 때문에 함수 body 가(함수 스코프) 최상위 스코프가 됩니다.
그래서 위 그림처럼 count 와 value가 모두 함수의 local 스코프에 존재하게 됩니다.
그런데 아래와 같이 기본값 매개변수를 사용하면 얘기가 달라집니다.
const test2 = (value = 10) => {
let count = value;
debugger;
}
test2();
기본값을 갖는 value는 함수 body 의 부모 스코프에서 초기화가 되고 그걸로 값을 초기화하는 count 변수는 local이 아닌 block 스코프에 존재하게 됩니다.
사실 함수에 기본값 매개변수만 있고 그 값을 실제로 사용하지 않아도 함수 body 안에 선언하고 정의한 변수의 스코프가 변경됩니다.
const test2 = (value = 10) => {
let count = 999;
debugger;
}
test2();
또는
const test2 = (value = 10) => {
var count = 999;
debugger;
}
test2();
이 내용을 비슷하게 흉내를 내 보면 아래와 같습니다. 억지로 var 변수로 바꾸고 초기화를 다른 블럭에서 해 본 것입니다. (사실 위 내용으로 인해 var count = value 나 let count = value 나 상관 없습니다. 😅)
const test3 = (value = 10) => {
var count;
{
count = value;
}
debugger;
}
test3();
함수 스코프를 갖도록 var count 를 선언했는데 count 는 그림과 같이 block 스코프에 존재합니다. 기본값 매개변수를 사용했기 때문에 var count 의 스코프가 변경되었기 때문입니다. 이 상태에서 inner 클로저를 정의하고 호출해 보면
const test4 = (value = 10) => {
var count;
{
count = value;
}
const inner = () => {
console.log(count); // block
debugger;
};
inner();
}
test4();
그림과 같이 block 스코프에 있는 count 를 사용하게 됩니다. 이 block 스코프는 inner 함수가 도달할 수 있는 상위 스코프이기 때문에 따로 변수를 캡쳐해서 클로저 스코프를 만들지 않습니다.
이 것은 아래와 같이 Script 스코프에 있는 변수 a를 inner에서 참조할 때, inner 가 Script 스코프에 도달 할 수 있기 때문에 클로저 스코프를 만들지 않는 이유와 동일합니다.
let age = 10;
const test5 = (value = 10) => {
var count;
{
count = value;
}
const inner = () => {
console.log(age); // script
console.log(count); // block
debugger;
};
inner();
}
test5();
정리해 보면, 아래와 같을 것 같습니다.
1. 기본값 매개변수를 사용하면 기본값 매개변수를 초기화하는 스코프가 함수 body 안의 변수 스코프에 영향을 준다.
기본값 매개변수를 사용하든 사용하지 않든 함수 안의 변수는 block 스코프에 존재하게 되는데 클로저가 스코프 체인을 통해서 접근할 수 있는 상위 스코프이기 때문에 따로 변수 캡쳐를 위한 클로저 스코프를 만들지 않는다.
정리 그림: 기본값 매개변수 사용할 때
정리 그림: 기본값 매개변수를 사용하지 않을 때
답변이 도움이 되었기를 바래봅니다. 감사합니다. 🙂
const test5 = (value) => {
value = 5; // local scope
{ // 기존 함수 body
let count = 0;
const inner = () => {
console.log(value); // closure scope
console.log(count); // block scope
debugger;
};
inner();
debugger;
}
};
test5();
답변 주신 내용 천천히 읽어보면서 다시 정리해봤습니다. 기본값 매개변수를 흉내낸 코드에 대해서 더 생각해봤는데, 이 코드가 좀 더 가깝지 않을지 조심스레 여쭤봅니다 ㅎ
기존 함수 body의 상위 스코프에 value가 정의된 거니 기존 함수 body 전체를 block으로 감싸고, 그 상위 스코프인 함수의 local scope에 value를 초기화해봤습니다.
처음에는 함수 body보다 상위 스코프에 기본값 매개변수가 선언돼야 하니까 함수의 local 스코프에 count 변수가 선언되고, local 스코프의 상위 스코프에 value가 선언되어야 하지 않나 생각했는데 위 코드처럼 생각하니까 확실히 이해가 되더라구요.
튜브님 감사합니다! 😍 그리고 제안해 주신 내용으로 수정 보완해 주셔서 다시 한번 감사드려요. 네 튜브님이 제안해 주신 코드가 정확한 코드입니다. 쵝오! 👍👍👍👍👍
튜브님 코드에 한 숟가락 얹어서 좀 만 보태볼게요. 일반 함수로 변경해서 arguments 를 사용해서 기본값을 사용할 때와 사용하지 않을 때도 적용해 보았습니다.
function test5() {
// 기본값 파라미터 초기화하는 스코프
let initialValue = arguments.length > 0 ? arguments[0] : 10;
{
// 원래 함수의 스코프
let count = initialValue;
const inner = () => {
console.log(count); // block
debugger;
};
inner();
}
}
test5();
test5(999);
튜브님 멋진 질문과 수정 보완 피드백 주셔서 다시 한번 감사드려요! 👍🙇♂ 아마 다른 분들에게도 도움이 많이 되는 내용일 것 같습니다! 언제나 해피 코딩 하셔요!😊
사랑합니다