blog

Javascript Major Concepts

  • javascript
  • Hoisting
  • Execution Context
  • Closure
  • Scope
  • TDZ
  • [JS] 자바스크립트의 The Execution Context (실행 컨텍스트) 와 Hoisting (호이스팅) 블로그 글을 보면서 다시 정리한 글입니다. 위의 블로그 글은 Understanding Execution Context and Execution Stack in Javascript를 번역하고 정리한 글입니다.

    자바스크립트의 스코프, 클로저, TDZ, 실행컨택스트, 호이스팅 등의 개념들은 나눠서 이해해야 하는 것이 아니라 한번에 이해해야 되는 개념들입니다.

    실행 컨텍스트 (Execution Context)자바스크립트 코드가 실행되고 연산되는 범위를 나타내는 추상적인 개념입니다. 코드가 실행된다면 실행 컨텍스트 내부에서 실행되고 있는 것입니다. 실행 컨텍스트가 어떻게 만들어지고 변수와 함수는 어느 범위 안에서만 사용가능한지 알아보겠습니다.

    함수 호출 시마다 만들어지는 독립적인 메모리 공간으로서 해시맵을 실행컨텍스트(Execution Context)
    EC 란 언어론적인 관점으로 보자면 어휘공간(lexical environment)
    특정 변수를 찾기 위한 격리된 메모리 공간을 어휘공간(lexical environment)

    실행 컨텍스트와 실행 스택(Execution Stack) 개념은 자바스크립트에서 호이스팅, 클로져, 스코프 같은 개념을 이해하는데 매우 중요한 내용입니다.
    자바스크립트 엔진은 코드를 실행하기 위해서 실행에 필요한 여러가지 정보를 알고 있어야 합니다. 실행에 필요한 여러가지 정보란 다음과 같습니다.

    • 변수 : 전역변수, 지역변수, 매개변수, 객체의 프로퍼티
    • 함수 선언
    • 변수의 유효범위
    • this

    이에 같이 실행에 필요한 정보를 형상화하고 구분하기 위해서 자바스크립트 엔진은 실행 컨텍스트를 물리적 객체의 형태로 관리합니다.

    Execution Context

    실행 컨텍스트는 쉽게 말해 코드가 실행되고 있는 구역, 범위에 대한 개념입니다.

    Types of Exection Context

    • Global Execution Context : 가장 베이스가 되는 실행 구역입니다. 특정 '함수' 안에서 실행되는 코드가 아니라면 코드는 전역 컨텍스트에서 실행됩니다. 전역 컨텍스트에서는 두 가지 일이 이루어집니다. 1) window 오브젝트인 전역 컨텍스트를 생성하고 2) this 를 global object로 할당합니다.
    • Functional Execution Context : 함수가 호출될 때마다, 해당 함수에 대한 실행 컨텍스트가 생성됩니다. 각각의 함수들은 독립적인 실행 컨텍스트를 가지며, 실행 컨텍스트는 함수가 호출이 되어야 만들어집니다.
    • Eval Function Execution Context : eval 함수 또한 독립적인 실행 컨텍스트를 가진다. 하지만 eval는 자바스크립트 개발자가 많이 사용하지 않는 개념으로 여기서는 설명하지 않겠습니다.

    Execution Stack (호출 스택)과 함수 실행 순서

    다른 프로그래밍 언어에서 불리는 호출 스택과 Execution Stack은 같은 말입니다. 스택은 LIFO(Last in, First out) 자료 구조로 코드가 실행하면서 만드는 실행 컨텍스트들이 저장되는 구조입니다.

    자바스크립트 엔진이 script tag를 처음 만나면 전역 컨텍스트를 만들고 현재 실행되고 있는 호출 스택에 이를 push합니다. 다른 함수가 호출 되면 해당 함수에 대한 실행 컨텍스트를 생성하고 스택에 push합니다.

    script tag를 만났을 때 어떻게 동작하는지 자세히 확인하려면 예제로 이해하는 JobQueue와 Event Loop를 살펴보면 됩니다.

    자바스크립트 엔진은 실행 컨텍스트가 호출 스택에서 가장 위에 있는 함수를 실행한다. 함수가 할 일을 마치면 스택에서 제거됩니다(pop).

    Execution Context in Detail

    지금까지 자바스크립트 엔진이 어떻게 실행 컨텍스트를 관리하는지 알아보았습니다. 다음은 자바스크립트 엔진이 실행 컨텍스트를 만드는 과정에 대해서 알아보겠습니다.

    실행 컨텍스트는 두 가지 단계로 생성됩니다. 1) Creation Phase2) Execution Phase 입니다.

    (1) Creation Phase

    Creation phase 동안 두 가지 일이 일어납니다.

    1. LexicalEnvironment 컴포넌트 생성
    2. VariableEnvironment 컴포넌트 생성

    실행 컨텍스트는 개념적으로 다음과 같습니다.

    ExecutionContext = {
      LexicalEnvironment = <ref. to LexicalEnvironment in memory>,
      VariableEnvironment = <ref . to VariableEnvironment in memory>,
    }

    Lexical Environment

    Lexical Environment는 identifier-variable mapping 되는 곳입니다.
    참조 대상 식별자인 identifier는 함수와 변수의 이름과 같이 어떤 대상을 다른 대상과 구분하여 식별할 수 있는 유일한 이름입니다. 자바스크립트는 이 규칙대로 식별자를 찾습니다. 즉 Lexical Environment에서는 변수와 해당 변수에 대입된 값이 매핑되는 곳이라고 할 수 있습니다. 매핑만 됩니다.

    스코프참조 대상 식별자(identifier)를 찾아내기 위한 규칙입니다. 자바스크립트는 이 규칙대로 식별자를 찾습니다.

    • 전역 스코프 : 코드 어디에서든지 참조할 수 있습니다
    • 함수 스코프 : 함수 코드 블록이 만든 스코프로 함수 자신과 하위 함수에서만 참조할 수 있습니다.

    프로그래밍 언어는 두 가지 방식으로 함수의 상위 스코프를 결정합니다. 첫번째 방식을 동적 스코프(Dynamic scope)라 하고, 두번째 방식을 렉시컬 스코프(Lexical scope) 또는 정적 스코프(Static scope)라 합니다. 자바스크립트를 비롯한 대부분의 프로그래밍 언어는 렉시컬 스코프를 따릅니다.
    정리하면 렉시컬 스코프는 함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 결정됩니다. 반대로 동적 스코프는 함수를 어디서 호출했는지에 따라 결정된다.
    var a = 20;
    var b = 40;
    
    function foo(){
    	console.log('bar');
    }

    위의 코드에 대한 lexical environment는 아래와 같습니다.

    lexicalEnvironment = {
      a : 20,
      b : 40,
      foo : <ref. to foo function>
    }

    렉시컬 환경은 세가지 일을 합니다. (VariableEnvironment도 동일)

    1. Environment Records
    2. Reference to the outer environment
    3. This binding

    1. Environmnet Records
    lexical environment 안에 함수와 변수를 기록합니다.

    Declarative environment record : 변수와 함수 선언을 저장하는 곳입니다.
    Object environment record : 전역 코드에 대한 lexical environment는 objective environment records를 포함합니다. 변수와 함수의 선언과 다르게 object environment record는 글로벌 오브젝트도 기록합니다. 각각의 객체의 속성을 바인딩하기 위해서 record에 새로운 엔트리가 형성됩니다.

    2. Reference to the outer environment
    외부 환경으로의 참조 값의 의미는 외부 lexical 환경으로 접근할 수 있다는 의미입니다. 자바스크립트 엔진이 현재의 lexical environment에서 변수를 찾지 못했다면 외부 환경에서 해당 변수를 찾아 볼 수 있다는 의미입니다.

    자바스크립트는 렉시컬 스코프를 따르며, 참조 대상 식별자를 찾아나설 때 the outer environment의 참조값을 외부 환경에 접근하면서 찾아가는 것입니다.

    3. This binding
    this의 값이 여기서 결정됩니다. 글로벌 실행 컨텍스트에서 this는 global object입니다.
    함수 실행 컨텍스트에서는 this 값은 어떻게 함수가 호출되었는지에 따라 달라집니다. 만약 함수가 object reference로 호출되었다면 this는 해당 객체를 가리키게 됩니다. 그렇지 않으면 this는 글로벌 객체(window)를 가리키거나 strict mode에서는 undefined를 가리키고 있습니다.

    this와 함수 호출

    const person = {
      name : 'peter',
      birthYear : 1994,
      calcAge(){
    	console.log(2018- this.birthYear);
      }
    }
    person.calcAge();
    const calcuateAge = person.calcAge;
    calculateAge();

    person.clacAge() calcAge는 'person' object reference로 호출되었기 때문에 여기서 "this"는 'person'을 가리키게 됩니다.
    calculateAge() 여기서는 주어진 객체 참조값이 없기 때문에 this는 글로벌 window 객체를 가리키게 됩니다.

    this의 동적바인딩

    함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정됩니다.

    1. 함수 호출
    2. 메소드 호출
    3. 생성자 함수 호출
    4. apply/call/bind 호출
    • 내부함수는 일반 함수, 메소드, 콜백함수 어디에서 선언되었든 관계없이 this는 전역객체를 바인딩합니다.
    클로져(closure)

    “A closure is the combination of a function and the lexical environment within which that function was declared.” - MDN


    클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말합니다. 이를 조금 더 간단히 말하면 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수라고 말할 수 있습니다.


    클로저에 의해 참조되는 외부함수의 변수를 자유변수(Free variable)라고 부릅니다. 클로저라는 이름은 자유변수에 함수가 닫혀있다(closed)라는 의미로 의역하면 자유변수에 엮여있는 함수라는 뜻입니다.

    여기까지가 실행 컨텍스트의 LexicalEnvironment에 관한 내용이었습니다.
    다음은 2) VariableEnvironment에 대한 내용입니다.

    VariableEnvironment

    LexicalEnvironment 와 funtion, 변수 식별자가 binding 되는 점을 포함해 동일합니다. VariableEnvironment 또한 Lexical Environment입니다. ES6에서 LexicalEnvironment와 VariableEnvironment 둘의 차이점은 전자가 함수 선언과 변수 (let과 const)의 바인딩을 저장하고 후자는 변수 var만 저장한다는 점입니다.

    ✅ 정리
    실행 컨텍스트 Creation Phase에서는
    Scope Chain, 변수, 함수, 인자들을 만듬.
    this 를 결정.
    자바스크립트 엔진의 Syntax Parser가 코드를 읽으면서 컴퓨터가 알아들을 수 있는 언어로 변환.
    자바스크립트 엔진은 코드를 읽으면서 변수와 함수의 선언된 것을 찾고 메모리에 해당 변수와 함수를 저장. (호이스팅)


    호이스팅이란 var 선언문이나 function 선언문 등 모든 선언문이 해당 Scope의 선두로 옮겨진 것처럼 동작하는 특성을 말한다. 즉, 자바스크립트는 모든 선언문(var, let, const, function, function*, class)이 선언되기 이전에 참조 가능하다.


    ex) 함수 선언식(function declaration) -> function foo(){}, 함수 표현식(function expression) -> const foo = function(){}

    (2) Execution Phase

    드디어 자바스크립트 엔진이 한줄 한줄 위에서 부터 코드를 읽으면서 코드를 실행할 차례입니다. 이 단계에서 가장 중요한 것은 선언했던 변수들에 값이 할당되는 것입니다.

    코드를 통해서 앞에서 보았던 실행 컨텍스트의 Creation Phase, Execution Phase 과정을 이해해보겠습니다.

    let a = 20;
    const b = 30;
    var c;
    
    function multiply(e, f){
     var g = 20;
      return e * f * g;
    }
    c= multiply(20,30);
    1. 자바스크립트 엔진은 script를 만나면 전역 컨텍스트를 만듭니다. 전역 변수 a,b, multiply는 LexicalEnvironment에 c는 VariableEnvironment에서 이름: 값 매핑이 됩니다.
    2. 전역 컨텍스트의 Execution Phase에서 코드가 실행되고 각각의 변수에 값이 할당됩니다.
    3. multiply() 함수가 호출되는 순간 multiply() 함수의 실행컨텍스트가 만들어집니다. 외부 환경으로는 전역 컨텍스트를 참조하고 있고, object reference로 호출되지 않았기 때문에 여전히 this는 global object입니다.
    4. multiply 함수가 값을 리턴하면서 호출 스택에서 빠집니다. 전역 컨텍스트에 있던 전역 변수 c에 값이 업데이트됩니다. 그 후 글로벌 코드가 끝나면 프로그램도 종료됩니다.

    Note

    Javascript 인터프린터 내부에서 변수는 총 3단계를 걸쳐 생성됩니다.

    • 선언 (Declaration): 스코프와 변수 객체가 생성되고 스코프가 변수 객체를 참조.
    • 초기화(Initalization): 변수 객체가 가질 값을 위해 메모리에 공간을 할당. 이때 초기화되는 값은 undefined.
    • 할당(Assignment): 변수 객체에 값을 할당.

    let과 const는 실행 컨텍스트가 만들어질 때 (Creation phase) 어떤 값도 가지고 있지 않습니다. 하지만 var는 undefined를 가지고 있습니다. (1.선언)

    실행 컨텍스트가 만들어지는 동안, 코드는 변수와 함수 선언을 위해 스캔됩니다. 이때 함수의 선언은 environment에 함수 전체가 다 저장되지만, 변수들은 기본 값으로 undefined나 아직 초기화 되지 않은 상태로 저장됩니다. (이 때 let, const 키워드로 생성된 변수들이 TDZ(Temporal Dead Zone)구간에 들어갔다고 합니다.)

    TDZ(Temporal Dead Zone)의 변수 객체의 상태는 선언은 되어있지만 아직 초기화가 되지않아 변수에 담길 값을 위한 공간이 메모리에 할당되지 않은 상태 (단, var로 선언된 변수는 선언과 동시에 초기화가 됨. )

    이 때문에 var 변수가 선언되기 전에 undefined라는 값으로 접근 할 수 있는 것이고 let과 const를 선언하기 전에 접근하면 reference error를 얻게 되는 것입니다.

    Execution phase동안 자바스크립트 엔진이 소스 코드에서 let 변수의 값이 선언된 곳을 찾지 못하면 undefined를 할당한다.

    참고
    [JS] 자바스크립트의 The Execution Context (실행 컨텍스트) 와 Hoisting (호이스팅)
    Understanding Execution Context and Execution Stack in Javascript
    https://poiemaweb.com/js-this
    Javascript의 let과 const 그리고 TDZ

    Top
    Copyright©2022 Klog(blackbell) all right reserved