본문 바로가기
Javascript

Javascript의 스코프(Scope)에 대한 이해

by kmmguumnn 2018. 4. 29.

Javascript에서 어떤 함수실행되면, 새로운 runtime scope가 생성된다. 이 scope는 함수의 context를 규정하며, 즉 어떤 함수에서 어떤 변수들이 사용 가능한지를 규정하게 된다.

그렇다면 함수가 접근할 수 있는 영역은 어디까지일까? 이와 관련된 개념인 스코프(scope)에 대해 알아보자.



Scope

함수의 runtime scope는 주어진 함수 내에서 사용 가능한 변수를 일컫는다. 함수 내부의 코드는 다음에 대해 접근 가능하다:


1. 함수의 인자(argument)

2. 함수 내부에 선언된 지역 변수

3. 부모 함수의 scope로부터의 변수

4. 전역 변수



다음의 코드를 보자.

const myName = 'Kim';
// 전역 변수(Global variable)

function introduceMyself() {

  const you = 'student';
  // introduce()가 정의된 곳과 같은 곳에 선언된 변수 you

  function introduce() {
    console.log(`Hello, ${you}, I'm ${myName}!`);
  }

  return introduce();
}


introduceMyself() 함수 내부에 introduce() 함수가 정의되어 있는데, 이 introduce() 함수는 인자를 받지도 않고 지역변수도 없다. 대신 you라는 변수는 부모 함수 scope에 있는 것을 참조하고, myName이라는 변수는 더 올라가서 전역 변수를 참조하게 된다. 그래서 "Hello, student, I'm Kim!"이라는 문자열이 출력된다.




Javascript는 Function-Scoped다

Javascript에서 scope가 함수와 밀접하게 연관되어 있는 것을 볼 수 있는데, 왜 그럴까?

전통적으로 Javascript에서는, 변수는 block의 scope보다는 함수의 scope 내에서 정의된다. 함수에 접근하는 것은 scope를 바꾸고, 함수 내에 정의된 변수들은 함수 바깥에서는 접근할 수 없다.




스코프 체인 (Scope Chain)

함수 호출동안에 변수를 참조하고자 할 때, Javascript interpreter는 항상 그 함수의 지역 변수를 먼저 찾는다. 찾는 것이 없으면, 위쪽으로 찾아 나간다(Scope Chain). 다음의 코드를 보자.

function one() {
  two();
  function two() {
    three();
    function three() {
      // 함수 three()의 코드는 여기에
    }
  }
}

one();

one()이 호출되면, 다른 모든 중첩된 함수들도 같이 호출될 것이다. 위에서 말했듯이 가장 안쪽부터 시작해 바깥쪽으로 차례로 호출된다. (three() → two() → one(), 그래도 없으면 → window)

이런 방식으로 가다보면, three() 함수는 위쪽에 있는 변수와 함수들에 접근할 뿐만 아니라, one() 바깥에 선언된 전역 변수까지도 접근할 것이다.


아래의 그림으로 정리해보자.


어떤 변수를 사용하고자 할 때, Javascript 엔진은 중첩된 자식 함수의 지역 변수를 맨 먼저 찾는다. 만약 있으면 그 값을 쓰지만, 없다면 그 변수가 있을 때까지 바깥 범위를 계속 찾는다. Global scope까지 갔는데도 해당 변수가 없다면, 값은 undefined가 된다.

이렇게 안에서부터 바깥으로 찾아나가는 것을 "Scope Chain"이라고 한다.




Variable Shadowing

만약 scope chain에 있는 어떤 변수와 같은 이름의 변수를 만들면 어떻게 될까? 이 경우 Javascript는 딱히 에러를 내뱉지는 않는다.

이름이 같은 두 변수 중 local scope에 있는 변수가 바깥 scope에 있는 변수를 "shadow"한다. 즉 덮어쓴다. 다음의 코드를 보자.

const symbol = '¥';

function displayPrice(price) {
  const symbol = '$';
  console.log(symbol + price);
}

displayPrice('80');
// '$80'

전역 변수로 symbol이 있고 '¥'이 할당되어 있는데, displayPrice() 함수 내에 또 symbol이라는 변수가 있고 '$'라는 다른 값이 할당되어 있다. displayPrice() 함수를 호출하면 '$'가 적용된 값이 출력된다.

JavaScript interpreter는 어떤 'symbol'의 값을 써야할지 어떻게 알까? '$'를 가리키는 symbol이 함수 내부에 선언되어 있으므로, 바깥 scope에 있는 모든 symbol들은 다 무시한다. 그래서 '$'가 적용된 것이다.


각각 다른 context에서 같은 이름의 변수들이 선언되어 있을 경우, 안쪽에서 바깥쪽 scope로의 scope chain에서 가장 먼저 찾아지는 값을 쓰게 된다. 즉 같은 이름을 가진 모든 지역변수는 바깥 scope의 변수에 비해 항상 우선순위를 갖는다.







참조할 것


댓글