호이스팅, 스코프, 클로저 그리고 실행 컨텍스트

Dongwon Ahn·2024년 2월 17일
1

JS & TS 학습

목록 보기
4/7
post-thumbnail

스터디를 위해 자바스크립트의 실행 컨텍스트에 대해 학습을 하다가, 주로 면접을 위해 학습했던 호이스팅, 스코프, 클로저가 실행 컨텍스트와 연관성이 높구나를 깨달았습니다.
그렇기에 해당 글에서는 호이스팅, 스코프, 클로저의 개념에 대해 가볍게 이야기하며, 실행 컨텍스트에 대해 알아보겠습니다.

호이스팅이란?

호이스팅은 인터프리터가 코드를 실행하기 전에 함수, 변수, 클래스 또는 import의 선언문을 해당 범위의 맨 위로 끌어올리는 것 처럼 보이는 현상을 말합니다.

console.log(test); // undefined
var test = 'hello world';

위 코드와 같이 자바스크립트는 함수 안에서 필요한 변수들을 모아서 유효 범위의 최상단에 선언합니다. 선언만 위로 끌어 올려지며, 할당은 끌어 올려지지 않습니다.

let과 const는 위 코드와 같이 사용할 경우 ReferenceError를 발생합니다.

console.log(test); // ReferenceError를
let test = 'hello world';

즉 let과 const로 선언된 변수는 var와 다르게 선언하기 이전에 사용할 수 없습니다. 그렇기 때문에 주로 js, ts에서는 var대신 let, const를 사용합니다.

다만, let과 const가 호이스팅이 되지 않는 다는 것은 아닙니다. TDZ(일시적 사각지대)에 영향을 받기 때문에 선언된 위치 전에 참조할 경우 에러가 발생합니다.

스코프란?

스코프는 직역하면 범위 라는 뜻입니다.
javascript에서 스코프는 변수에 접근할 수 있는 범위를 말합니다.
자바스크립트 스코프는 전역(global), 지역(local) 2가지 타입이 있습니다.

전역 스코프는 전역에 선언되어있어 어느 곳에서든지 해당 변수에 접근할 수 있으며, 지역 스코프는 해당 지역에서만 접근할 수 있어, 지역을 벗어난 곳에서는 접근할 수 없습니다.

var gloablVariable = 11; // 전역 스코프

function localFunc() { // 지역 스코프
	var localVariable = 1;
  	console.log(localVariable); // 1
  	console.log(gloablVariable); // 11
}
localFunc();
console.log(gloablVariable); // 11
console.log(localVariable); // Uncaught ReferenceError

위 예시 코드를 봤을 때 전역 스코프에 선언된 전역 변수인 gloablVariable는 지역 스코프에서 정상적으로 접근할 수 있는 반면,
지역 스코프에 선언된 지역 변수인 localVariable는 전역 스코프에서 접근 시 ReferenceError 에러가 발생하는 것을 볼 수 있습니다.

클로저란?

클로저는 내부 함수가 외부 함수의 스코프에 접근할 수 있게 하는 JavaScript의 특성입니다. 제가 개인적으로 이해한 클로저는 자신이 선언될 당시의 환경을 기억하는 함수 입니다.
클로저는 실행 컨텍스트의 Lexical Environment를 통해 이러한 스코프 체인을 유지합니다.

function outerFunction() {
    var outerVar = 'I am outside!';

    function innerFunction() {
        console.log(outerVar);
    }

    return innerFunction;
}

var myInnerFunction = outerFunction();
myInnerFunction(); // 'I am outside!'

Execution Context (실행 컨텍스트)란?

실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체입니다.
자바스크립트는 동일한 환경에 있는 환경 정보들을 모은 실행 컨텍스트를 콜스택에 쌓아올린 후 실행하여 코드의 환경과 순서를 보장할 수 있게 됩니다.

let a = "Notion posting";

function first(){
	console.log("Inside first function");
	second();
	console.log("Again inside first function");
}

function second(){
	console.log('Inside second function');
}

first();
console.log('Inside Global Execution Context');

예제 코드의 실행 컨텍스트 흐름
처음 자바스크립트 코드를 실행하는 순간 전역 컨텍스트가 콜 스택에 담깁니다. 전역 컨텍스트는 따로 코드의 실행 없이 자바스크립트 파일이 열리는 순간 전역 컨텍스트가 활성화됩니다. 브라우저의 경우 window, node 환경의 경우 global 같은 객체를 사용할 수 있는 있습니다.

실행 컨텍스트의 구성

실행 컨텍스트 내부에는 variable environment, lexical environment, this binding 가 있습니다.

  • variable environment: 실행 컨텍스트가 생성될 때, 해당 컨텍스트 내에서 선언된 변수와 함수를 기록하는 환경입니다. 이 환경은 변수와 함수 선언의 스코프와 호이스팅을 관리합니다. 선언 시점의 lexical environment의 스냅샷으로 변경 사항은 반영되지 않습니다.
  • lexical environment: 처음에는 variable environment와 같지만 변경 사항이 실시간으로 반영됩니다.
  • this binding: this 식별자가 바라봐야 할 대상 객체를 의미합니다.

variable environment와 lexical environment은 식별자 정보 environmentRecord, 외부 환경 정보 outerEnvironmentReference가 포함되어 있습니다. 초기화 과정 중에는 사실상 완전히 동일하지만, 이후 변경사항에 따라 달라지게 됩니다.

environmentRecord 란 현재 컨텍스트와 관련된 식별자와 식별자에 바인딩된 값이 기록되는 공간입니다. 더불어 실행 컨텍스트 내부 전체를 처음부터 끝까지 확인하며 순서대로 수집합니다. 그렇기 때문에 코드가 실행되기 전에 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명을 알고 있게 됩니다. 즉 호이스팅이 일어나게 됩니다.

outerEnvironmentReference는 상위 스코프를 가리킵니다. 즉 현재 environmentRecord보다 바깥에 있는 environmentRecord를 참조합니다. 코드 상에서 어떤 변수에 접근하려면 현재 컨텍스트의 lexical environment를 탐색 후 발견되면 해당 값을 반환하고, 발견하지 못한 경우 outerEnvironmentReference에 담긴 lexical environment를 탐색하는 과정을 가집니다. (그렇기 때문에 탐색하는 위치에서 가까운 변수가 우선순위가 높은 것을 알 수 있습니다.)

만약 전역 컨텍스트에서도 변수를 찾을 수 없다면 var로 호이스팅이 된 경우 undefined을 반환하고, 그 외 선언이 안되어 있거나, let const인 경우 에러가 발생합니다.


참고 자료

profile
Typescript를 통해 풀스택 개발을 진행하고 있습니다.

0개의 댓글