컨텍스트, 렉시컬환경, 클로저

milkbottle·2024년 7월 7일
post-thumbnail

저것들이 다 뭐지?

javascript는 코드를 한줄 한줄 읽어가며 실행한다.

컴파일 언어가 아니라 스크립트 언어이기 때문이다.

한줄 한줄 읽을 때 마다 그 상태를 저장하는데 context라고도 표현한다.

Context

현재 코드가 실행되는 상황을 의미한다.

함수 실행 컨텍스트, 글로벌 실행 컨텍스트가 있다.

우리가 흔히 아는 {} 브레이스 마다 컨텍스트가 만들어진다고 생각하면 쉽다.

이 컨텍스트에는 this, arguments, 선언된 변수, 선언된 함수, 제어 흐름의 위치 들이 저장되어 있다.

보통 this를 컨텍스트라고 많이 말하는데, 맞는 말이긴 한데 정확히 말하면 위의 요소들이 모두 포함되어 있는 것이다.

종류

  • 전역 컨텍스트(Global Context)
    전역 코드에서 this는 전역 객체를 가리킨다.
    (브라우저에서는 window, Node.js에서는 global)
  • 함수 컨텍스트(Function Context)
    함수 내부에서 this는 기본적으로 전역 객체를 가리키지만, 엄격 모드('strict mode')에서는 undefined가 된다.
  • 객체 메서드 컨텍스트(Object Method Context)
    객체의 메서드에서 this는 그 메서드를 소유한 객체를 가리킨다.
  • 생성자 함수 컨텍스트(Constructor Function Context)
    생성자 함수에서 this는 새로 생성된 인스턴스를 가리킨다.
  • call, apply, bind를 통한 명시적 컨텍스트(Explicit Context Binding)
    call, apply, bind 메서드를 사용해 this를 명시적으로 설정할 수 있다.

Context Call Stack


컨텍스트는 콜 스택으로 구성되어서 호출된다.

위의 코드를 예시로 들어보자.

  1. var c = 5 변수를 글로벌 컨텍스트 push
  2. multiply() 함수 호출로 인해 함수 컨텍스트 push
  3. random() 함수 호출로 인해 함수 컨텍스트 push
  4. return 10 으로 함수를 벗어나 함수 컨텍스트 pop
  5. return c * random() 으로 함수를 벗어나 함수 컨텍스트 pop
  6. multiply()함수 종료로 마지막 코드가 끝났으므로, 글로벌 컨텍스트 pop

Lexical Environment

렉시컬 환경은 실행 컨텍스트 의 자식 집합의 개념으로, 2가지로 이루어져 있다.

구성요소

  • 환경 레코드(Environment Records)
    현재 실행 중인 코드 블록이나 함수 내부에서 선언된 모든 식별자(변수, 함수, 매개변수 등)를 저장하고 관리하는 구조이다.
  • 외부 렉시컬 환경(Outer Lexical Environment)
    자기 부모의 렉시컬 환경을 참조 하는 것이다. 쉽게 말하면, graph의 인접리스트 이런 것이라고 봐도 무방하다.

예시


컨텍스트와 거의 유사한 계층 구조로 이루어진다.

왜냐면 컨텍스트 내부에 렉시컬 환경이 있는 포함집합의 개념이기 때문이다.

글로벌 렉시컬 환경에서는 outerFunction() 객체가 있으므로, 환경레코드는 저렇게 된다.

그리고 글로벌 렉시컬 환경은 최상위 컨텍스트에 존재하기 때문에, 외부렉시컬 환경 또한 없다.

outerFunction 렉시컬 환경, innerFunction 렉시컬 환경 또한 같은 언리로 뿅뿅 하고 만들어진다!

Closure

내가 아는건 클로저스 밖에 모르는데

클로저란 렉시컬 환경을 응용해서, 해당 렉시컬 환경이 사라졌음에도 이를 기억하고 사용하는 것을 의미한다.

말이 너무 어려운가? 예시로 설명해보겠다.

예시

글로벌 스코프 기준으로 살펴보자.

const newFunction = outerFunction('outside');

첫 번째 줄로 인해 outerFunction 렉시컬 환경이 만들어진다.

이때, arugments로 'outisde'가 들어간다.

newFunction('inside');

두 번째 줄에서 innerFunction 렉시컬 환경이 만들어진다.

이 때, arguments로 'inside'가 전달된다.

그런데 innerFunction 렉시컬 환경은 outerFunction() 를 외부렉시컬 환경을 가지는 것이 아니라, outerFunction('outside') 로 가지는 것임을 알아야 한다.

두 개의 의미는 다르다.

이를 통해 글로벌 렉시컬환경에서도 innerFucntion 렉시컬 환경으로 바로 접근할 수 있다.

은닉화

클로저를 통해 데이터를 은닉화할 수 있다.

function createCounter() {
    // 은닉화된 변수
    let count = 0;

    // 클로저를 사용하여 count 변수에 접근하는 함수들
    return {
        increment: function() {
            count++;
            console.log(count);
        },
        decrement: function() {
            count--;
            console.log(count);
        },
        getCount: function() {
            return count;
        }
    };
}

const counter = createCounter();

counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1
console.log(counter.getCount()); // 1

외부에서는 count 변수에 접근할 수 없지만, 마치 클래스처럼 이를 사용할 수 있다.

이것이 바로 클로저를 사용하는 이유이다.

참고

0개의 댓글