본문 바로가기

자바스크립트

[모던자바스크립트 Deep Dive] 13. 스코프

1. 스코프(scope)란?

스코프 : 식별자가 유효한 범위

 

모든 식별자(변수 이름, 함수 이름, 클래스 이름 등)는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위가 결정된다.

 

var x = 'global';

function foo() {
  var x = 'local';
  console.log(x); // ①
}

foo();

console.log(x); // ②

다음 예제에서 코드의 가장 바깥 영역(전역)과 foo 함수 내부에 같은 이름을 갖는 변수 x를 선언했고, ①과 ②에서 x 변수를 참조하는데 자바스크립트 엔진은 여기서 두 변수 중에서 어떤 변수를 참조해야 할 것인지를 결정한다.(식별자 결정)

 => 스코프는 자바스크립트 엔진이 식별자를 검색할 때 사용하는 규칙이라고도 할 수 있다.

 

스코프라는 개념이 없었다면?

식별자는 어떠한 값을 구별하여 식별해 낼 수 있는 고유한 이름이다. 그러므로 식별자는 어떠한 값을 구별해 낼 수 있어야 하기 때문에 유일해야 한다. => 하나의 값은 유일한 식별자에 연결되어야 한다.

그렇게 된다면 식별자는 중복이 안되기에 식별자 명을 명명하는 것이 매우 번거롭고 힘들었을 것이다!

 

결국, 스코프를 통해 변수 이름의 충돌을 방지하여 같은 이름의 변수를 사용할 수 있도록 한다.

 

2. 스코프의 종류

구분 설명 스코프 변수
전역 코드의 가장 바깥 영역 전역 스코프 전역변수
지역 함수 몸체 내부 지역 스코프 지역변수

코드는 전역과 지역으로 구분할 수 있는데, 전역은 코드의 가장 바깥 영역을 말하고 지역은 함수 몸체의 내부를 말한다.

 

여기서 변수는 자신이 선언된 위치에 의해서 스코프가 결정된다.

=> 전역에서 선언된 변수 : 전역 스코프

=> 지역에서 선언된 변수 : 지역 스코프

 

전역 스코프와 지역 스코프

var x = "global x";
var y = "global y";

function outer() {
  console.log(x)	//global x
  var z = "outter's local z";
  var x = "inner x"
  
  console.log(x)	//inner x
  console.log(y)	//global y
  console.log(z)	//outter's local z
}
console.log(x)	//global x
console.log(z)	//ReferenceError : z is not defined

전역 스코프

'전역'은 코드의 가장 바깥 영역을 말하는데, 전역은 전역 스코프를 만든다.

전역스코프를 가진 전역 변수는 코드의 어디든 참조할 수 있다.

위 예제에서 x변수는 outer 함수 내부에서도, 외부에서도 어디든 참조할 수 있다.

 

지역 스코프

'지역'은 함수 몸체 내부를 말한다. 지역은 지역 스코프를 만드는데, 이 지역스코프는 자신이 선언된 지역과 하위지역(중첩함수)에서만 유효하다.

위 예제에서 z변수는 지역스코프를 가지는 변수로서 outer함수 내부에서는 참조할 수 있지만 outer 함수 바깥에서는 참조할 수 없다!

 

3. 스코프 체인

스코프 체인 : 스코프가 계층적으로 연결된 것

 

변수를 참조할 때 자바스크립트 엔진은 스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하며 선언된 변수를 검색한다.

 

이는 다른 말로 상위 스코프에서 유효한 변수는 하위 스코프에서 자유롭게 참조가 가능하지만, 하위 스코프에서 유효한 변수를 상위 스코프에서 참조할 수 없다는 의미이다.

 

4. 함수 레벨 스코프

C나 자바 등의 프로그래밍 언어는 함수 몸체뿐만 아니라 모든 코드 블록(if문, for문, while문 등)이 지역 스코프를 만든다.

 => 블록 레벨 스코프

 

그러나 자바스크립트에서 var로 만든 변수는 오로지 함수의 코드 블록만을 지역 스코프로 인정한다.

 => 함수 레벨 스코프

var x = 1;

if (true) {
  // var 키워드로 선언된 변수는 함수의 코드 블록(함수 몸체)만을 지역 스코프로 인정한다.
  // 함수 밖에서 var 키워드로 선언된 변수는 코드 블록 내에서 선언되었다 할지라도 모두 전역 변수다.
  // 따라서 x는 전역 변수다. 이미 선언된 전역 변수 x가 있으므로 x 변수는 중복 선언된다.
  // 이는 의도치 않게 변수 값이 변경되는 부작용을 발생시킨다.
  var x = 10;
}

console.log(x); // 10

이렇게 if같이 코드블록 안에서는 지역 스코프로 인정되지 않아 의도치 않게 값이 변경될 수 있다.

 

하지만 ES6에서 도입된 const, let 키워드는 블록 레벨 스코프를 지원하니 되도록이면 let, const 키워드를 사용하도록 하자!

 

5. 렉시컬 스코프

렉시컬 스코프 : 함수를 어디서 정의했는지에 따라 함수의 상위 스코프를 결정하는 방식

 

var x = 1;

function foo() {
  var x = 10;
  bar();
}

function bar() {
  console.log(x);
}

foo(); // ?
bar(); // ?

위 예제에서 bar함수는 전역에서 정의된 함수이다. bar함수는 코드가 실행되기 전에 평가되어 함수 객체를 생성하므로 bar함수는 전역 스코프를 기억하게 된다. 그러면 bar함수가 호출된 곳이 어디든 전역 스코프를 상위 스코프로 사용하기 때문에 1을 두 번 출력하게 된다.

 

자바스크립트는 렉시컬 스코프를 따르므로 함수를 어디서 호출했는지가 아니라 함수를 어디서 정의했는지에 따라서 상위 스코프를 결정한다!