스코프란 식별자의 유효 범위를 말한다.
선언된 변수는 어느 범위까지 유효할까? 어디서나 접근이 가능하면 안될까? 스코프가 필요한 이유는 아래와 같다.
let global = 30;
function scope() {
let local = 21;
console.log(global);
};
scope(); // 30
console.log(local); // Uncaught ReferenceError: local is not defined
scope라는 함수의 내부에선 변수 global의 값을 참조할 수 있다. 모든 코드 블럭에서 접근하여 값을 알아낼 수 있는 변수를 전역 변수라고 한다.
반면 외부에서 변수 local에 접근하여 값을 콘솔창에 출력하려니 참조 오류(Reference Error)가 발생한다.
이처럼 코드 블럭 안에서 선언되어 전역에서 접근할 수 없는 변수를 지역 변수라고 한다.
블록, 함수 레벨 스코프는 참조 가능한 유효 범위의 경계를 조건문, 반복문을 포함한 코드 블럭{ }으로 할지, 오직 함수의 코드 블럭으로 할지의 차이이다. 성에 비유하자면, 어떤 것을 성벽으로 삼을지 정하는 것과 같다.
성벽 안에서 쉽게 바깥을 관측할 순 있지만 반대는 어려운 것 처럼, 스코프 내부에서 바깥을 참조하는 것은 가능하지만 전역에서 스코프 내부의 값을 참조하는 것은 불가능하다.

var로 선언된 변수와 함수는 함수 내부만 스코프로 인정해주며, 다른 코드 블럭의 변수와 함수는 전역 변수, 전역 함수로 간주한다.
function sayHi(name) {
if (name) {
var greet = `Hi, ${name}!`;
}
console.log(greet);
}
sayHi('Wonkook');
// "Hi, Wonkook!"
위와 같이 console.log 명령어가 조건문 바깥에 있음에도 greet의 값을 참조할 수 있는 것을 확인할 수 있다.
ES6부터 등장한 let과 const로 선언된 변수는 블록 레벨 스코프를 지원한다.
function sayHi(name) {
if (name) {
let greet = `Hi, ${name}!`;
}
console.log(greet);
}
sayHi('Wonkook');
// Uncaught ReferenceError: greet is not defined
함수 레벨 스코프와 달리 조건문을 포함한 모든 실행 블럭을 스코프로 간주하기 때문에 greet을 참조할 수 없게 된다.
스코프는 필요한 영역에 한정하여 유효 범위가 좁을수록 좋다.
높은 망루에선 낮은 위치의 성채 내부와 바깥에 뭐가 있는지 관찰(참조)할 수 있다. 그러나 성벽에 시야가 가리기 때문에 바깥에서 내부를 관측할 수 없다. 함수의 코드 블럭은 성벽과 같다. 코드 블럭 안쪽에 있을 수록 바깥을 관측할 순 있지만 코드 블럭 바깥에서 내부의 변수를 참조할 순 없다.
블록 스코프에서 지역 변수 y는 전역 변수 x값을 참조할 수 있고, 반환되는 함수의 지역 변수z는 y의 값을 참조할 수 있다.
높은 망루(내부 코드 블럭)에 있더라도 완전히 다른 코드 블럭에 속해있는 지역 변수를 참조할 순 없다. 코드 블럭의 실행은 독립적이며 실행이 끝나면 더 이상 참조할 수 없기 때문에 더욱이 다른 위치에서 참조할 수 없다.
자신이 속해있는 지역의 변수들을 참조할 수 있게 되며, 해당 코드 레벨에 참조값이 없다면 상위 레벨의 스코프로 참조 값을 찾아 나가는 현상을 스코프 체인(Scope Chain)이라고 한다. 전역 스코프에도 참조값이 없다면 null을 반환하게 된다.
자바스크립트는 렉시컬 스코프 원칙을 따른다. 렉시컬 스코프는 또 다른 말로 정적 스코프(Static Scope)라고 한다.
렉시컬 스코프란 함수를 호출한 곳이 아닌 선언한 곳을 기준으로 스코프를 결정하는 원칙이다.
let greet = 'Hello';
function sayHi() {
let greet = 'Hi';
print();
}
function print() {
console.log(greet);
}
sayHi(); // 예상: "Hi" | 출력값: "Hello"
print(); // 예상: "Hello" | 출력값: "Hello"
print는 sayHi 코드 블럭 내부에서 호출되기 때문에 가장 가까운 지역 변수 greet의 값 'Hi'를 참조할 것 같지만, 전역 변수 greet의 값 'Hello'를 참조하고 있다.
만약 동적 스코프(Dynamic Scope)를 따랐다면, print 속의 greet은 자신을 호출한 함수 sayHi 내부의 greet 값을 참조했을 것이다.

글과 이미지
Wonkook Lee ⓒ All Rights Reserved
🙏🏻 잘못된 정보가 있다면 지적해주세요
안녕하세요. 글 잘 읽고 많은 도움을 받아갑니다. JavaScript를 공부하는 학생으로서 궁금한 점이 한 가지 있는데 스코프체인 부분에서 전역 스코프에도 참조값이 없다면 null을 반환한다고 적혀있는데, 일반적으로 참조값이 없다면 레퍼런스 에러를 발생시키는게 아닌가 하는 궁금증에 댓글을 남기게 되었습니다.