TIL 9. JS scope과 scope chain

javascript의 스코프에 대해서 알아보겠습니다. 이 글은 코어자바스크립트 책을 토대로 쓰여졌습니다.

scope

scope는 식별자에 대한 유효범위이다.

Execution Context vs Scope

The first important thing to clear up is that context and scope are not the same.

Every function invocation has both a scope and a context associated with it. Fundamentally, scope is function-based while context is object-based. In other words, scope pertains to the variable access of a function when it is invoked and is unique to each invocation. Context is always the value of the this keyword which is a reference to the object that “owns” the currently executing code.

outerEnvironmentReference

식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해나가는 것. 그리고 이를 가능케 하는 것이 LexicalEnvironment의 두번째 수집자료인 outerEnvironmentReference이다.

outerEnvironmentReference는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조한다.

선언하다라는 행위가 실제로 일어날 수 있는 시점이란 call stack상에서 어떤 실행 컨텍스트가 활성화된 상태일 뿐이다. 어떤 함수를 선언 혹은 정의하는 행위 자체도 하나의 코드에 지나지 않으며, 모든 코드는 실행 컨텍스트가 활성화 상태일 때 실행되기 때문이다.

outerEnvironmentReference는 연결리스트 형태를 띈다. 선언 시점의 LexicalEnvironment를 계속 찾아 올라가면 마지막엔 전역 컨텍스트가 있을 것이다. 가장 가까운 요소부터 차례대로만 접근할 수 있다.

그림으로 보면, 호출하는 함수 안으로 들어갈수록 점차 규모가 작아지는 반면 스코프 체인을 타고 접근 가능한 변수의 수는 늘어난다. 한 마디로, 가장 안에 있을수록 더 많은 변수에 접근할 수 있다.

variable shadowing : 이런 구조적 특성 덕분에 여러 스코프에서 같은 이름의 식별자를 선언한 경우엔 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에게만 접근 가능하다.

block scope
if else 블록은 scope를 따로 만들게 되지만, call stack에는 안 쌓인다. 해당 실행 컨텍스트의 call stack으로 유지됨.

try & catch의 경우 catch는 catch scope이 따로 있어서, error을 미리 인자값처럼 받고 시작한다.

scope chain

자바스크립트 엔진은 각각의 scope에 일종의 리스트 형태로 global execution context와 그 위에 쌓인 함수의 스코프의 레퍼런스를 순서대로 저장한다.
스코프 체인은 식별자 중에서 객체(전역 객체 제외)의 프로퍼티가 아닌 식별자, 즉 변수를 검색하는 메커니즘이다.

javascript does this internally to search it in other Outer Environment until they found that variable.
Everyone can access the Scope in the Global Execution Context.
자바스크립트는 해당 변수를 찾을 때까지 바깥 environment에서 내부적으로 검색한다. 그리고 바깥 scope에서 내부 scope의 변수에는 접근할 수 없다.

엔진은 스코프 체인을 통해 렉시컬 스코프(Lexical Scope)를 확인한다. 이렇게 순차적으로 리스트를 따라가며 검색하기 때문에 스코프 체인이라고 불린다. 마지막까지 검색에 실패하면 참조 에러가 발생한다.

예제 : 뭔가 이상한 변수 참조

const a = "Hello";

const bar = () => { 
	const c = "Hello c";
    // (3)
    console.log(a)
    console.log(b)
    console.log(c)
}; 

const foo = ()=>{ 
	bar() 
    const b = "Hello b";
    // (2)
    console.log(a)
    console.log(b)
    console.log(c)
}; 

foo(); 

// (1)
console.log(a)
console.log(b)
console.log(c)

지금 모든 함수에서 변수 a,b,c에 접근하고 있다. 그리고 javascript engine의 원칙에 의해서 이 코드에는 총 3개의 에러가 생긴다.

(1) global execution context

변수 a는 console.log함수를 통해 Hello로 잘 출력된다. 그러나 변수 b와 c의 경우엔 에러만 나온다.

Uncaught ReferenceError : b is not defined
Uncaught ReferenceError : c is not defined

(2) foo함수의 execution context

변수 a와 b는 잘 출력된다. 그러나 변수 c는 에러와 함께 출력되지 않는다.

Uncaught ReferenceError : c is not defined

(3) bar 함수의 execution context

변수 a와 b와 c 모두 잘 출력된다.

전역 변수는 자제하자

코드의 안전성을 위해선 global variable의 사용을 최소화해야 한다. 최대한 각기 다른 실행 컨텍스트를 만들어서, 변수 호출이 겹치는 일이 없도록 해야 한다.

좋은 웹페이지 즐겨찾기