본문 바로가기
공부 정리/You Don't Know Js

[YDKJ] Scope & Closures - Illustrating Lexical Scope

by 경적필패. 2023. 5. 8.
반응형

요약

Chapter 2: Illustrating Lexical Scope

챕터1에서는 스코프가 어떻게 코드 컴파일중에 결정되는지 알아보았다.

이 모델을 렉시컬 스코프라 칭하였음.

 

이번 장에서는 다양한 방식으로 스코프를 설명하고 프로그램을 더 정확하게 이해할 수 있도록 하는 것이 목표다.

 

Marbles, and Buckets, and Bubbles... Oh My!

// outer/global scope: RED

var students = [
    { id: 14, name: "Kyle" },
    { id: 73, name: "Suzy" },
    { id: 112, name: "Frank" },
    { id: 6, name: "Sarah" }
];

function getStudentName(studentID) {
    // function scope: BLUE

    for (let student of students) {
        // loop scope: GREEN

        if (student.id == studentID) {
            return student.name;
        }
    }
}

var nextStudent = getStudentName(73);
console.log(nextStudent);   // Suzy

해당 코드를 통해 블록영역을 알 수 있습니다.

  • JS엔진은 컴파일하면서 변수의 선언을 발견하면 어떤 스코프에 포함되는지 확인함.
  • 런타임에는 선언되지 않은 변수의 스코프를 조회함. 위 예시에서 students의 경우 Green스코프에 없고, BLUE스코프에도 없으므로 RED스코프까지 올라감.
  • JS엔진이 런타임 도중에 스코프를 결정하는 건 아님. => 컴파일중에 이미 블록스코프가 정해진 상태에서 런타임에 확인하는 개념.

 

A Conversation Among Friends

JS엔진 맴버

  • 엔진 : JS의 시작부터 끝까지 컴파일 및 실행을 담당함
  • 컴파일러 : 엔진의 친구 중 하나로, JS의 구문분석 및 컴파일을 담당함.
  • 스코프매니저 : 마찬가지로 엔진의 친구로, 선언된 모든 변수/식별자의 검색 목록을 수집 및 유지하며, 현재 실행중인 코드가 이에 어떻게 접근할지 규칙을 강제합니다.

JS가 동작하는 방식을 정확히 이해하려면 위의 맴버들을 잘 알아야함!

 

var students = [
    { id: 14, name: "Kyle" },
    { id: 73, name: "Suzy" },
    { id: 112, name: "Frank" },
    { id: 6, name: "Sarah" }
];

function getStudentName(studentID) {
    for (let student of students) {
        if (student.id == studentID) {
            return student.name;
        }
    }
}

var nextStudent = getStudentName(73);

console.log(nextStudent);
// Suzy

처음의 var students를 선언하는 부분은 하나의 부분 같지만 실은 2가지로 나뉩니다.

1. 일단 컴파일러는 코드를 의미있는 단위로 쪼개 AST를 형성함.

2. 그 후 컴파일러는 students에 메모리를 할당하고 배열을 변수에 할당하고. 그 다음 엔진이 실행한다.

 

정확히는 다음과정을 따른다

컴파일러가 students 변수를 만나면, 스코프매니저에게 students 변수가 이미 존재하는지 확인함

이미 존재하면 넘어가고 아니라면, 스코프 매니저에게 새로운 변수 등록을 요청함

그 후 컴파일러는 엔진을 위해 핸들링할 코드를 생성하고, 엔진은 스코프매니저에게 해당 변수가 있는지 물어보고 없으면 다른 범위에서 계속 찾음(스코프범위)

 

이를 대화문으로 풀어보자면...

 

컴파일러 : global scope manager야, students라는 식별자에 대한 선언을 발견했어 들어봤니?

global scope manager : 아니 처음 들었어 그래서 지금 만들었어

컴파일러: global scope manager야, getStudentName에 대한 선언을 발견했어 들어본적 있니?

global scope manager :  아니 처음 들었지만, 만들었어

컴파일러: global scope manager야, getStudentsName은 함수를 가리키기 때문에 새로운 scope bucket이 필요해.

function scope manager : 여깄어 scope bucket

컴파일러 : function scope manager야, studentId라는 매개변수 선언을 발견했어 들어봤어?

function scope manager: 아니 지금 만들었어

컴파일러 : function scope manager야 for-loop를 위한 scope bucket이 필요해

 

이 과정은 컴파일러와 scope manager가 스코프안에 변수를 생성하는 과정임

 

 

 

이렇게 스코프안에 변수를 생성 한 이후에는, 엔진이 실행시킴

엔진 : global scope manager야, getStudentName 식별자를 찾아서 이 함수와 연관 시켜줄래?

global scope manager: 좋아 여기 있어

엔진 : global scope manager야, students라는 참조를 발견했는데 들어봤어?

global scope manager: 응, 이 스코프에 선언되어 있어

엔진: 고마워 나는 students를 undefined로 초기화해서 사용준비를 할거야.

....

 

이러한 과정을 요약하면

1. 컴파일러는 scope 변수의 선언을 설정합니다.

2. 엔진은 scope manager에게 변수를 찾도록 요청하고 undefined로 초기화한 다음에, 값을 할당합니다.

 

Nested Scope

스코프는 임의의 깊이로 중첩될 수 있음.

전역스코프 => getStudnetName 함수 스코프 => for 루프 스코프

만약 스코프가 요청하는 식별자를 찾을 수 없다면...

엔진 : 함수 스코프 매니저야! students에 대한 소스 참조가 있어 들어봤니?

함수 스코프 매니저 : 아니 이건 처음들어 봤어. 바깥 스코프를 참고해봐

엔진 : 글로벌 스코프 매니저야! students에 대한 소스 참조를 들어봤니.

글로벌 스코프 매니저 : 응 이건 선언됐었지. 여기!

 

 

Lookup Failures

 

엔진이 중첩된 모든 스코프를 탐색해도 식별자를 찾을 수 없으면 오류가 발생함.

변수가 source라면 => 선언되지 않은 변수로 간주되어 ReferenceError가 발생함

변수가 target이라면 => 마찬가지로 ReferenceError가 발생함

 

 

  • not defined와 undefined는 다름!!! => not defined는 선언되지 않은 것.
  • 그러나 JS의 typeof는 두 경우 모두 undefined를 뱉음. => 주의!!!

 

Global... What!?

  • strict모드를 사용하지 않으면 전역스코프에 선언되어 있지 않더라도 스코프 매니저가 새로 만들어줌.
  • 전역변수에 의존하지 마라!!

 

Building On Metaphors

중첩 스코프를 시각화 할 때 빌딩이 좋다!

 

1층이 현재 실행중인 스코프이며,

건물의 맨 위층은 전역 스코프임.

 

타겟 또는 소스 참조를 해결할 때, 먼저 현재층에서 찾고, 그곳에서 찾지못하면 외부스코프를 찾아가며 전역스코프까지 찾아가는 방식임.

끝까지 못찾으면 ReferenceError

 

 

 

느낀 점

평소 스코프에 대한 개념은 있었기 때문에, 현재에서 참조하지 못할때 상위 스코프로 가는 것은 알고 있었다.

근데 이책을 통해 엔진, 컴파일러, 스코프매니저를 함께 스코프를 이해하니 더 깊게 이해할 수 있었다.

특히 컴파일 단계와, 런타임 단계에서의 엔진, 컴파일러, 스코프매니저의 역할을 알 수 있었다.

반응형

댓글