해당 포스팅은 위키북스의 "모던 자바스크립트 Deep Dive"라는 책을 독학하며 기록하는 글입니다.


스코프란?

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

var x = 'global';

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

foo();			//local
console.log(x);		//global

위의 예제와 같이 같은 x임에도 불구하고 어디서 선언됐으며, 어디서 호출되냐에 따라 다른 결과값이 나오게 된다. 이것이 바로 스코프 때문이다.

"코드가 어디서 실행되며 주변에 어떤 코드가 있는지"렉시컬 환경이라고 부른다. 즉, 코드의 문맥은 렉시컬 환경으로 이뤄진다. 이를 구현한 것이 "실행 컨텍스트"이며, 모든 코드는 실행 컨텍스트에서 평가되고 실행된다. - p191 코드의 문맥과 환경

이러한 스코프의 종류로는 전역지역이 있다. 전역에서 선언된 변수는 전역 스코프를 가지며 전역 변수라 한다. 마찬가지로 지역에서 선언된 변수는 지역 스코프를 가지며 지역 변수라 한다. 이때, 지역이라함은 함수 몸체 내부를 뜻하며(나중에 let, const를 공부하면 블록단위로 바뀐다.) 지역 변수는 자신의 지역 스코프과 하위 지역 스코프에서만 사용할 수 있다.


스코프 체인

그럼 자바스크립트 엔진은 동일한 이름의 변수가 있을 경우, 어떻게 어디에 해당하는 변수인지 판단하고 실행하는 걸까?

바로 스코프 체인을 검사하여 해당 변수를 찾아 실행하는 것이다. 스코프 체인이란, 전역에서 지역으로, 지역에서 하위 지역으로 계속해서 블록이 중첩되어 내려갈 때, 각 지역에서 선언한 변수들을 정리하고 하위에서 상위로 체인같이 연결해 놓은 개념이다. (실제 물리적으로도 존재)

이렇게 체인으로 연결된 스코프 체인을 보고 변수가 사용되는 시점에 어느 단계에서 사용됐는지 확인하고, 해당 스코프에 변수가 있다면 해당 지역의 변수를, 없다면 상위 단계로 검사를 이어나가면서 동일한 이름의 처음 만난 변수를 사용하게 된다.

예를 들어보자. 전역에서 x에 one을 y에 two를 선언하고, 함수A를 만들었다. 함수A에서는 z에 three를 선언하고, 함수B를 만들었다. 그리고 함수B안에 x를 다시 four라고 선언했다. 이때의 스코프 체인을 생각해보면 다음과 같다.

전역: x = one, y = two, 함수A

	^

함수A: z = three, 함수B

	^
    
함수B: x = four

그리고 함수B 스코프에서 x를 참조하면 해당 체인의 함수B 스코프부터 x를 찾게 되고, 바로 four를 반환하게 된다.
이어서 z를 참조하면, 동일하게 함수B 스코프에서 먼저 찾고, B에서 없으므로 한 단계 위인 함수A 스코프를 찾게 된다. 여기에 z값이 있으므로 three를 반환하게 된다. 스코프 체인의 원리는 이렇게 되는 것이다.


함수 레벨 스코프

지역은 함수 몸체 내부를 말하고 지역은 지역 스코프를 만든다고 했다. 이는 코드 블록이 아닌 함수에 의해서만 지역 스코프만 생성된다는 의미다.

C나 자바 등을 비록한 대부분의 프로그래밍 언어는 함수 몸체만이 아니라 모든 코드 블록이 지역 스코프를 만든다. 이러한 특성을 블록 레벨 스코프라 하고, 함수의 코드 블록만을 지역 스코프로 인정하는 것을 함수 레벨 스코프라고 한다.

이후에 나오는 let, const 키워드는 블록 레벨 스코프를 지원하므로 나중에 알아보도록 하자.


렉시컬 스코프

자바스크립트는 렉시컬 스코프를 따르므로 함수를 어디서 호출했는지가 아니라 함수를 어디서 정의했는지에 따라 상위 스코프를 결정한다고 한다. 함수가 호출되 위치는 상위 스코프 결정에 어떠한 영향도 주지 않는다. 즉, 함수의 상위 스코프는 언제나 장신이 정의된 스코프다. 다음의 예제를 보도록 하자.

var x = 1;

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

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

foo();		//1
bar();		//1

위의 예제에서 bar를 호출하면 1이 찍히는 것이 당연하다. 하지만 foo를 호출하면 foo안에서 x를 재정의하고 bar를 호출했는데도 1이 찍히는데 이러한 이유가 바로 위의 설명이다.
bar를 호출한 위치는 비록 foo의 안일지라도, 정의한 위치는 전역이기 때문에 bar는 전역 스코프를 따르고, 스코프 체인을 검사할 때 전역부터 x를 찾아나가기 때문이다.

profile
I Will be Relaxed Person

0개의 댓글