자바스크립트의 스코프


1. 스코프 (Scope)


스코프란 식별자의 유효 범위를 말한다.

선언된 변수는 어느 범위까지 유효할까? 어디서나 접근이 가능하면 안될까? 스코프가 필요한 이유는 아래와 같다.

  • 의도치 않은 변수 값의 변조를 방지
    특히 외부 API 라이브러리와 연동하여 사용할 때 같은 변수명끼리 충돌이 생기는 것을 방지할 수 있다.
  • 효율적인 메모리 관리
    코드 블럭 안의 참조값들은 코드가 실행 될 때만 메모리에 할당되는 것이 효율적이다.

1-1. 전역 변수 (Global Variables)와 지역 변수 (Local Variables)

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)가 발생한다.
이처럼 코드 블럭 안에서 선언되어 전역에서 접근할 수 없는 변수를 지역 변수라고 한다.

1-2. 함수-레벨 스코프 (Function-Level Scope)와 블록-레벨 스코프 (Block-Level Scope)

블록, 함수 레벨 스코프는 참조 가능한 유효 범위의 경계를 조건문, 반복문을 포함한 코드 블럭{ }으로 할지, 오직 함수의 코드 블럭으로 할지의 차이이다. 성에 비유하자면, 어떤 것을 성벽으로 삼을지 정하는 것과 같다.
성벽 안에서 쉽게 바깥을 관측할 순 있지만 반대는 어려운 것 처럼, 스코프 내부에서 바깥을 참조하는 것은 가능하지만 전역에서 스코프 내부의 값을 참조하는 것은 불가능하다.

1-2-1. 함수 레벨 스코프

var로 선언된 변수와 함수는 함수 내부만 스코프로 인정해주며, 다른 코드 블럭의 변수와 함수는 전역 변수, 전역 함수로 간주한다.

function sayHi(name) {
  if (name) {
    var greet = `Hi, ${name}!`;
  }
  console.log(greet);
}

sayHi('Wonkook');
// "Hi, Wonkook!"

위와 같이 console.log 명령어가 조건문 바깥에 있음에도 greet의 값을 참조할 수 있는 것을 확인할 수 있다.

1-2-2. 블록 레벨 스코프

ES6부터 등장한 letconst로 선언된 변수는 블록 레벨 스코프를 지원한다.

function sayHi(name) {
  if (name) {
    let greet = `Hi, ${name}!`;
  }
  console.log(greet);
}

sayHi('Wonkook');
// Uncaught ReferenceError: greet is not defined

함수 레벨 스코프와 달리 조건문을 포함한 모든 실행 블럭을 스코프로 간주하기 때문에 greet을 참조할 수 없게 된다.
스코프는 필요한 영역에 한정하여 유효 범위가 좁을수록 좋다.


1-3. 스코프 체인 (Scope Chain)

높은 망루에선 낮은 위치의 성채 내부와 바깥에 뭐가 있는지 관찰(참조)할 수 있다. 그러나 성벽에 시야가 가리기 때문에 바깥에서 내부를 관측할 수 없다. 함수의 코드 블럭은 성벽과 같다. 코드 블럭 안쪽에 있을 수록 바깥을 관측할 순 있지만 코드 블럭 바깥에서 내부의 변수를 참조할 순 없다.
블록 스코프에서 지역 변수 y는 전역 변수 x값을 참조할 수 있고, 반환되는 함수의 지역 변수zy의 값을 참조할 수 있다.
높은 망루(내부 코드 블럭)에 있더라도 완전히 다른 코드 블럭에 속해있는 지역 변수를 참조할 순 없다. 코드 블럭의 실행은 독립적이며 실행이 끝나면 더 이상 참조할 수 없기 때문에 더욱이 다른 위치에서 참조할 수 없다.

자신이 속해있는 지역의 변수들을 참조할 수 있게 되며, 해당 코드 레벨에 참조값이 없다면 상위 레벨의 스코프로 참조 값을 찾아 나가는 현상을 스코프 체인(Scope Chain)이라고 한다. 전역 스코프에도 참조값이 없다면 null을 반환하게 된다.

1-4. 정적 / 렉시컬 스코프 (Static / Lexical Scope)

자바스크립트는 렉시컬 스코프 원칙을 따른다. 렉시컬 스코프는 또 다른 말로 정적 스코프(Static Scope)라고 한다.

렉시컬 스코프란 함수를 호출한 곳이 아닌 선언한 곳을 기준으로 스코프를 결정하는 원칙이다.

let greet = 'Hello';

function sayHi() {
	let greet = 'Hi';
  	print();
}

function print() {
	console.log(greet);
}

sayHi(); // 예상: "Hi" | 출력값: "Hello"
print(); // 예상: "Hello" | 출력값: "Hello"

printsayHi 코드 블럭 내부에서 호출되기 때문에 가장 가까운 지역 변수 greet의 값 'Hi'를 참조할 것 같지만, 전역 변수 greet의 값 'Hello'를 참조하고 있다.

만약 동적 스코프(Dynamic Scope)를 따랐다면, print 속의 greet은 자신을 호출한 함수 sayHi 내부의 greet 값을 참조했을 것이다.

글과 이미지
Wonkook Lee ⓒ All Rights Reserved

🙏🏻 잘못된 정보가 있다면 지적해주세요

profile
© 가치 지향 프론트엔드 개발자

1개의 댓글

comment-user-thumbnail
2023년 11월 21일

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

답글 달기