[책정리] CoreJavaScript 스코프, 스코프체인, outerEnvironmentReference

이진규·2022년 7월 19일
0
post-thumbnail

스코프, 스코프체인, OuterEnvironmentReference

Q. 스코프가 뭔가요?

스코프는 식별자에 대한 유효범위입니다. 식별자가 선언되고 할당된 값을 모든 코드에서 사용할 수 있는 것이 아닙니다. 사용할 수 있는 범위가 정해져 있습니다.

var a = 1;
var f = function(){
  var b = 3;
}

위의 예시에서 식별자 a는 function 밖에서도 사용 가능하지만 식별자 b는 function 안에서만 사용가능하며 밖에서 사용할 경우 선언되지 않았으므로 에러가 발생합니다. a의 스코프는 코드 전체라고 할 수 있고 b의 스코프는 function 내부라고 할 수 있습니다.

ES5까지는 전역 공간을 제외하고 오직 함수에 의해서만 스코프가 생성되었지만 ES6에서는 블록에 의해서도 생성되도록 변경되었습니다.

Q. 스코프체인은 뭔가요?

스코프 즉, 유효범위를 안->밖으로 차례대로 검색하는 것을 스코프체인이라 합니다. 이것은 LexicalEnvironment의 두번째 수집 자료인 outerEnvironmentReference를 통해 가능합니다.

Q. LexicalEnvironment는 뭐에요?

LexicalEnvironment는 필자의 말로 "사전적인 환경"이라고 해석했는데, 컨텍스트를 구성하는 환경 정보들을 모아둔 것이라고 볼 수 있습니다.
LexicalEnvironment는 environmentRecord와 outerEnvironmentReference로 구성되어 있습니다. 그런데 단어가 너무 길어서
LexicalEnvironment == "LE",
environmentRecord == "eR",
outerEnvironmentReference == 'oER'로 줄여서 쓰겠습니다.

다시 말해서, LE는 eR과 oER로 구성되어 있습니다. 그리고 컨텍스트를 구성하는 환경 정보들을 모아둔 것이기에 컨텍스트마다 있으며 컨텍스트는 함수의 범위 또는 전역범위라고 이해할 수 있습니다. 예를 들어

1 var a = 1;
2 function outer(){
3   function inner(){
4     console.log(a);
5     var a = 3;
6   };
7   inner();
8   console.log(a);
9 };
10 outer();
11 console.log(a);

위의 코드는 가장 밖의 코드 전체 부분이 있고 outer함수로 감싸진 부분과 inner함수로 감싸진 부분이 있습니다.
이 코드의 컨텍스트를 그림으로 표현하면 아래와 같습니다. [숫자]는 위의 코드의 줄번호를 의미합니다.

이 그림과 같이 위의 코드의 컨텍스트는 코드 전체 부분인 전역 컨텍스트, outer 컨텍스트, inner컨텍스트로 나뉘며 전역 안에 outer가 있고 outer 안에 inner가 있다고 볼 수 있으며 inner -> outer -> Global(전역) 이렇게 스코프체인으로 연결되어 있습니다.
여기서 스코프 체인을 이해할 수 있는데, 8번 줄에서 식별자 a를 log로 찍어보려고 할 때를 생각해봅시다.
1) inner안에서 a가 있는지 찾습니다. a가 선언된 적이 없기때문에
2) 스코프 체인으로 연결된 전역 컨텍스트를 확인합니다. 전역 컨텍스트의 LE의 eR에 a가 있기 때문에
3) 전역 컨텍스트의 a를 출력합니다.
이와 같이 현재 컨텍스트에서 식별자를 찾을 수 없을 때 연결된 스코프를 타고가서 다시 찾고 없으면 그 밖으로 가서 다시 찾는 과정을 거치게 됩니다.

Q. 그래서 oER은 뭐에요?

oER은 현재 호출된 함수가 선언된 과거의 LE를 참조합니다. 스코프 체인으로 연결되어 있다는 것이 결국 oER로 연결되어 있다는 것입니다. 그래서 여러 스코프에서 같은 이름의 변수를 선언했을 경우, 변수를 사용하려고 할 때 스코프 체인으로 연결되어있는 컨텍스트 중 가장 가까운 곳에서 선언된 변수를 사용하게 됩니다. 그래서 변수 은닉화라는 개념이 나옵니다.

Q. 변수 은닉화는 뭐에요?

여러 스코프에서 같은 이름의 변수를 선언했을 때, 가장 가까운 곳에서 선언된 변수를 사용하게 되므로 먼 곳에 있는 변수는 사용할 수 없게 되는 은닉 상태가 되게 됩니다.

1 var a = 1;
2 function outer(){
3   function inner(){
4     console.log(a);
5     var a = 3;
6   };
7   inner();
8   console.log(a);
9 };
10 outer();
11 console.log(a);

이 코드로 다시 보면 inner에서 a가 선언되어있기 때문에 전역 컨텍스트의 a는 사용할 수 없게 됩니다.

Q. 왜 이렇게 복잡해요?

간단한 코드인데도 그림을 그려보면 굉장히 복잡하게 나옵니다. 그래서 크롬에서 현재 실행 컨텍스트를 제외한 상위 스코프들에 대한 정보를 개발자도구의 콘솔을 통해 확인할 수 있습니다.

1 var a = 1;
2 function outer(){
3   function inner(){
4     console.dir(inner);
5     var a = 3;
6   };
7   inner();
8   console.log(a);
9 };
10 outer();
11 console.log(a);

위 코드의 4번째 줄처럼 확인하고싶은 컨텍스트의 함수 내부에서 console.dir(함수이름)하면 확인할 수 있습니다.

Q. 끝이에요?

마지막으로 전역변수와 지역변수라는 단어가 있는데, 전역변수는 계속 얘기했던 전역 컨텍스트에서 선언된 변수를 의미하고 지역 변수는 함수 내부에서 선언된 변수를 의미합니다. 전역변수는 어디에서나 사용될 수 있고 호이스팅에 의해 다른 변수로 덮어씌워질 수 있기 때문에 사용을 최소화하는 것이 좋습니다.

참고

정재남, ⌜코어 자바스크립트⌟, 위키북스, 2022, 36 - 64쪽

profile
개발자

0개의 댓글