Home JavaScript Scope
Post
Cancel

JavaScript Scope

✏️ 스코프란?

우선 스코프(Scope)란 단어의 뜻은 범위라는 의미를 가지고 있다. 자바스크립트에서 스코프(Scope)도 변수 참조의 유효 범위를 나타낸다. 스코프는 전역 스코프(Global scope)지역 스코프(Local scope) 두 가지로 구분할 수 있다.

  • 전역: 전역 범위에서 정의 되었으며, 어디서든지 참조할 수 있는 것

  • 지역: 함수 내에서 정의되어 함수 내에서만 참조할 수 있는 것

코드를 보면서 이해해보도록 하자. 아래 코드의 로그 출력 값은 어떻게 나올까?

1
2
3
4
5
6
7
8
9
10
// 예제 1
var foo = 'global';

function func1() {
  var foo = 'local';
  console.log(foo); // ?
}

func1();
console.log(foo); // ? 

변수 foo가 전역, 그리고 func1() 함수 내부에서 선언되어있는 상태다. func1() 함수 내부에 위치한 콘솔 로그는 func1() 함수 내부에서 선언된 변수 foo를 참조하게 된다. 반면에 전역에 위치한 콘솔로그는 전역에서 선언된 foo 변수를 참조한다.

이처럼 지역(함수) 내에서 선언된 변수는 지역(함수) 내에서만 참조할 수 있고, 함수 외부에서는 참조할 수 없다. 반대로 전역에서 선언된 변수는 어디에서나 참조할 수 있다. 이를 스코프(Scope)라고 하고 이는 곧 함수 레벨 스코프라고 한다.

자바스크립트의 스코프는 블록 레벨 스코프가 아닌 논 블록 레벨 스코프 다.

1
2
3
4
5
6
// 예제 2
if (condition) {
 var foo = true;
}

console.log(foo); // true

위 코드에서 중괄호 부분 { ... } 이 블록이다. 만약 저 if문이 전역에 선언되었다면, 블록 내에서 선언되었더라도 변수 foo는 전역 변수가 된다.

다만 ES6부터는 블록 레벨 스코프를 사용할 수 있는데, 간단하게만 예제 코드를 보고, 이 부분은 추후에 자세히 다루도록 하겠다.

1
2
3
4
5
if (condition) {
  let foo = true;
}

console.log(a); // a is not defined

차이점은 변수 선언 시, 키워드를 var가 아닌 let으로 선언했다. ES6 부터 let, const 라는 새로운 선언 방법이 있다.

🔗 스코프 체인 (Scope Chain)

지역 스코프는 함수 내에서만 정의된 것들을 참조할 수 있는 유효 범위라고 했는데, 그렇다면 함수 내부에 함수가 있다면 어떻게 될까? 아래 코드의 출력 값을 예상해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 예제 3
var foo = '전역';

function func1() {
  var foo = '지역';

  function func2() {
    console.log(foo); // ?
  }

  func2();
  console.log(foo); // ?
}

func1(); 

func2() 함수 내부의 콘솔 로그는 func1() 함수 내부에 선언된 foo변수를 참조하게 된다. func2() 내부에 선언된 foo 변수가 없는데 어떻게 상위 함수인 func1() 에서 선언된 변수를 참조하는 걸까?

자바스크립트는 변수를 참조할 때, 자신의 지역(스코프)에 선언된 변수를 찾게 된다. 만약 해당 지역에 변수를 못 찾게 된다면 상위 지역으로 올라가 변수를 찾는 성질이 있다. 이를 스코프 체인이라고 한다.

만약 전역 지역에도 참조 값이 없다면 Uncaught ReferenceError: foo is not defined 에러를 출력하게 된다.

📍 렉시컬 스코프 (Lexical Scope)

바로 위의 예제 2 코드를 아래 코드로 조금 변형해보았다. 아래 코드는 출력 값이 어떻게 될까?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 예제 4
var foo = '전역';

function func2() {
  console.log(foo); // ?
}

function func1() {
  var foo = '지역';

  func2();
  console.log(foo); // ?
}

func1();
func2();

이번 코드는 func2() 함수가 전역에서 선언되었고, func1() 함수 내부에서 호출이 되었다. 어차피 func2() 함수의 콘솔로그는 func1() 내부에서 호출 되었으니, func1() 의 변수 foo를 참조하지 않을까? 라는 생각이 들겠지만 그렇지 않다.

자바스크립트는 스코프를 정할 때, 함수가 어디서 호출되었는지가 아닌, 어디서 선언되었느냐에 따라 결정된다.

예제 3 코드를 보시면 func2() 함수는 호출은 func1() 내부에서 호출되었지만, 전역 스코프에 선언되었다. 위에서 자바스크립트는 자신의 스코프에서 선언된 변수를 먼저 참조한다고 했다. 따라서 마찬가지로 전역에 선언된 func2() 함수는 전역에 선언된 변수를 참조하게 된다. 이를 렉시컬 스코프(정적 스코프)라고 한다.

This post is licensed under CC BY 4.0 by the author.