자바스크립트에서 함수는 변수에 접근할 때 상위 함수로 연속해서 접근하며 값을 찾는다.
즉 변수를 참조할 때 하위 스코프에서 상위 스코프로 무조건 위로만 올라간다! 따라서 내부 함수에서 외부 함수를 참조할 수 있지만 반대는 불가능하다.
const x = "global x";
function outer() {
const y = "local y of outer func";
console.log(x); //global x
console.log(y); //local y of outer func
function inner() {
const x = "local x of inner func";
console.log(x); //local x of inner func
console.log(y); //local y of outer func
}
inner();
}
outer();
console.log(x); //global x
console.log(y); //Reference Error
inner 함수 내에서console.log(y)를 실행하려 할 때, 자신의 지역 스코프에서 먼저 값을 찾으려 하겠지만 변수 y에 해당하는 값이 없다.
그렇다면 자신의 바로 바깥 스코프인 outer의 지역 스코프 내에서 변수 y에 대한 값을 탐색한다.
여기서 const y = "local y of outer func"를 찾았기에, 콘솔에 local y of outer func이 찍히는 것이다.
그렇다면 이 코드에서 inner 함수 내부 변수 x 선언 라인이 없어지면 어떻게 바뀔까?
const x = "global x";
function outer() {
const y = "local y of outer func";
console.log(x); //global x
console.log(y); //local y of outer func
function inner() {
console.log(x); //global x
console.log(y); //local y of outer func
}
inner();
}
outer();
console.log(x); //global x
console.log(y); //Reference Error
inner 함수 내의 console.log(x)를 보자.
변수 x가 inner 지역 스코프에 없다. 그렇다면 위로 올라가 outer의 지역 스코프에서 값을 찾아본다. 여기에도 없다!
그렇다면 한 번 더 위로 올라가 이젠 전역 스코프(window)로 나간다. 여기서 const x = "global x" 라는 라인을 찾는다.
그 결과, 콘솔에 global x이 찍히는 것이다.
자바스크립트의 스코프는 함수를 호출할 때가 아니라 선언할 때 생긴다. 그렇기 때문에 한 스코프의 상위 스코프 또한 함수가 정의되는 시점에 결정된다.
let cat = "zero";
function log() {
console.log(cat);
}
function wrapper() {
cat = "nero";
log();
}
wrapper(); //nero
여기에서 결과는 nero 이다.
let cat = "zero";
function log() {
console.log(cat);
}
function wrapper() {
let cat = "nero";
log();
}
wrapper(); //zero
하지만 위 예시에서 결과는 zero이다.
log 함수 안의 cat이 자신이 선언되는 시점의 바깥인 let cat = "zero"을 참조하기 때문이다.
첫 번째 예시를 다시 보면, 마찬가지로 이 경우도 log 함수는 자신의 바깥인 let cat = "zero"을 참조한다. 하지만 콘솔이 찍히기 전 wrapper함수 내에서 cat이 nero로 재선언되었을 뿐이다.
💡 스코프 레벨의 종류
프로그래밍 언어에서 스코프 레벨은 크게 블록 레벨 스코프와 함수 레벨 스코프로 나뉜다.
블록 레벨 스코프에서는 if문, for문 그리고 함수 단위로 스코프를 나눌 수 있으며, 대부분의 프로그래밍 언어가 이에 속한다. 반면 함수 레벨 스코프에서는 이름에서도 알 수 있듯이 함수 단위로만 스코프가 쪼개진다. 자바스크립트는 함수 레벨 스코프 특성만을 가지고 있었지만, ES6에서 let과 const가 도입되면서 블록 레벨 스코프의 특성 또한 가질 수 있게 되었다.