본문 바로가기

자바스크립트

실행컨텍스트(Execution Context)

실행 컨텍스트(Execution Context)

  • 자바스크립트 코드가 실행되는 환경
  • 코드가 실행될 때 필요한 모든 정보를 담고 있는 추상적인 컨테이너

 

실행 컨텍스트의 구성 요소

JavaScript 를 실행하려면 컴파일과 실행이라는 두 단계가 필요하다.

JS 코드가 컴파일되면 실행 컨텍스트가 생성되고, 실행 컨텍스트가 준비되면 실행 단계가 시작된다.

컴파일 단계 (Compile) 실행 단계 (Excution)
변수와 함수 선언
실행 컨텍스트 생성
변수 환경 초기화
호이스팅
변수 할당
함수 실행
코드를 줄 단위로 실행
변수 환경 업데이트

 

⚠︎ 주의할 점

  • 같은 이름의 함수와 변수가 있을 경우 함수가 우선된다.
  • 조건문 내부의 변수 선언도 컴파일 단계에서 처리된다.

 

실행컨텍스트의 주요 4가지 구성요소

  • Variable environment
  • Lexical environment
  • Outer
  • this

변수 환경(Variable environment)과 렉시컬 환경(Lexical environment)

실행 컨텍스트 생성 시 변수는 두 환경에 동시에 생성된다.

초기 단계에 Variable Environment(VE)에 변수 정보를 저장한 다음, 이를 그대로 복사해 Lexical Environment(LE) 생성한다.

VE 는 초기 상태를 유지하고(스냅샷 역할), LE 는 코드 실행에 따라 변수 상태를 지속적으로 업데이트 한다.

 

Variable Environment

  • 초기 변수 상태를 저장
  • 호이스팅 관련 정보를 제공
  • 이후 변경사항은 Lexical Environment 에서 변수 상태를 관리한다.
  • 대부분 Lexical environment 와 동일하지만, let 과 const 는 약간 다르게 처리된다.

Lexical Environment

  • 현재 실행 중인 변수 및 함수 식별자를 관리(변수 참조 및 수정 등)
  • 스코프와 스코프 체인을 관리
  • Environment Record : 실제 변수와 함수 정보 저장
  • Outer Environment Reference : 상위 스코프 연결

 

❗️ 차이점

초기에는 VE LE 가 동일하지만 코드 진행에 따라 LE 만 변화하고, VE는 초기 상태를 유지한다.


실행컨텍스트의 동작

예시 1)

var apple = 10;
console.log(apple);

 

① 컴파일

 - 코드가 실행되면 첫번째로 컴파일이 되어 실행 컨텍스트가 생성된다

 -  apple 변수가 VE와 LE 에 저장된다.

 - 선언된 변수 apple 은 undefined 로 초기화

 

② 실행

- apple 변수에 숫자 10이 할당된다. (실제 메모리 값 변경)

- LE 에서 최신 변수 상태를 추적한다.

- console.log() 가 LE 에서 변수값 10을 읽어 출력한다.

 

종료

- 전체 실행 컨텍스트 제거

 

 

예시 2)

console.log(apple);
var apple = 10;

 

① 컴파일

- 첫 번째 줄은 변수 선언과 관련이 없으므로 건너뛴다.

- 두번째 줄의 apple 변수가 VE 와 LE 에 저장된다.

- apple 변수는 undefined 로 초기화 된다.

 

② 실행

- 첫 번째 줄에서 콘솔은 LE 에서 apple 변수를 찾아 undefined 값을 출력한다.

- 두 번째 줄에서 apple 변수에 10이 할당된다.

 

종료

 

✅ 주요 포인트

  • 변수 할당은 실제로 컴파일, 실행 두 단계로 나뉜다.
  • 컴파일 단계에서 Variable Environment 는 초기 변수 상태를 저장한다.
  • 실행 단계에서는 값 할당과 나머지 코드를 실행한다.

변수 호이스팅

console.log(x); // undefined
var x = 5;

console.log(y); // ReferenceError
let y = 10;

console.log(z); // ReferenceError
const z = 15;

 

var 호이스팅

  • 선언과 초기화가 컴파일 단계에서 같이 이루어진다.
  • 초기값은 undefined
  • 선언 전 접근 시 undefined 을 반환한다.

 

let / const 호이스팅

  • 선언만 컴파일 단계에서 이루어지고, 초기화는 실행 단계에서 이루어진다.
  • 선언과 초기화 사이의 구간을 Temporal Dead Zone(TDZ)이라고 하는데
  • TDZ 내에서 변수에 접근하면 ReferenceError가 발생한다.

 

var 와 let / const 의 주요 차이점

var let / const
함수 스코프, 호이스팅 시 undefined로 초기화 블록 스코프, 호이스팅 시 초기화되지 않음 (TDZ 존재)

 

 

✅ 주의사항

  • 가능한 let과 const를 사용하여 예측 가능한 스코프 동작을 유지하자

함수와 호이스팅

함수를 선언하는 주요 방법과 호이스팅의 차이에 대해 알아보자.

 

함수 선언식 (Function Declaration)

function greet(name) {
    return `Hello, ${name}!`;
}
  • function 정의부만 존재하고 별도의 할당 명령이 없는 것
  • 반드시 함수명이 정의되어있어야 함
  • 코드의 어느 위치에서든 호출 가능
  • 함수 선언식은 전체 함수가 호이스팅된다.

 

함수 표현식 (Function Expression)

// (익명) 함수 표현식 - 변수명 greet 가 곧 함수명
var greet = function(name) {
    return `Hello, ${name}!`;
};

// 기명 함수 표현식 - 변수명은 greet, 함수명은 sayHello
var greet = function sayHello(name) {
    return `Hello, ${name}!`;
};
  • 정의한 function 을 별도의 변수에 할당하는 형태
  • 함수명이 없어도 됨
  • (익명) 함수 표현식 - 간결하고 일반적으로 많이 사용됨
  • 기명 함수 표현식 - 재귀나 복잡한 로직에서 자기 참조가 필요할 때 유용
  • 함수 표현식은 변수 선언만 호이스팅되고, 함수 할당은 실행 시점에 이루어지므로 할당 이후에만 사용 가능

 

함수의 호이스팅

greet("hayoung"); // 정상 작동 (함수 선언식)
bye("hayoung");   // TypeError: bye is not a function (함수 표현식)

// 함수 표현식
var bye = function(name) {
    return `bye, ${name}!`;
};

// 함수 선언식
function greet(name) {
    return `Hello, ${name}!`;
}

 

① 컴파일

 - 함수 표현식인 bye 함수는 값이 undefined 로 할당되어 두 환경에 저장된다.

 - 함수 선언식인 greet 함수는 greet = funciton () {...} 함수 전체가 두 환경에 저장된다.

 

② 실행

 - 함수 선언식 greet은 전체 함수가 호이스팅되어 정상적으로 호출된다.

 - 함수 표현식 bye는 undefined로 초기화되어 호출 시 오류가 발생한다.

 

종료

 

 

❗️주의사항

  • 코드의 가독성과 예측 가능성을 위해 함수 선언은 코드 상단에 배치하자
  • 가능한 한 함수 선언식보다는 함수 표현식을 사용하자

 

❓함수 표현식을 권장하는 이유

  1. 스코프 관리: 함수 표현식은 스코프 제한 가능, 모듈화에 유리
  2. 호이스팅 문제 예방 : 예측가능한 코드 실행
  3. 가독성&유지보수 : 변수에 할당 시 코드 흐름과 함수 생성/사용 시점이 명확
  4. 유연한 활용 : 클로저, 고차 함수, 콜백 등에 적합
  5. ES6 화살표 함수와의 호환성 : 간결한 문법, this의 lexical 바인딩 제공
  6. 디버깅 용이성: 함수 생명주기 명확한 제어

 

✅  함수와 변수의 메모리 저장 방식(스택과 힙 개념과 연결되어 정리)

1. 함수저장

  •  함수 자체가 VE, LE 에 바로 저장되는게 아니라 함수 자체는 Heap 메모리에 저장되고, VE와 LE 에는 함수의 식별자(이름)와 Heap 메모리 참조 주소가 저장된다.

2. 변수저장

  •  원시타입의 작은 크기 데이터(숫자, 문자열, 불리언 등)는 값 자체가 VE, LE 에 직접 저장될 수 있고 참조 타입(객체, 배열 등)의 데이터는 Heap 메모리에 저장되고 VE, LE 에는 Heap 메모리 참조 주소가 저장된다.

스코프, 스코프 체인, outerEnvironmentReference

스코프(Scope)

식별자(변수와 함수) 가 접근 가능한 범위

 

ES5 까지의 자바스크립트에서는 전역공간과 함수에 의해서만 스코프가 생성되었다.

ES6 부터는 블록에 의해서도 스코프가 생성 될 수 있게 되어 { } 로 감싸진 블록 내에서도 독립적인 스코프가 형성된다.

블록 레벨 스코프는 let 과 const 로 선언된 변수에만 적용되고 var 로 선언된 변수는 여전히 함수 레벨 스코프를 따른다.

+ ES6 에서 도입된 class 선언과 strict mode 에서의 함수 선언도 블록 레벨 스코프를 따른다.

 

  • 전역 스코프 : 코드의 가장 바깥쪽 범위로, 어디서든 접근 가능
  • 함수 스코프 : 함수 내부에서 선언된 변수는 해당 함수 내에서만 접근 가능
  • 블록 스코프 : let 과 const 로 선언된 변수는 중괄호 { } 내에서만 접근 가능

 

스코프 체인(Scope Chain)

식별자(변수와 함수)를 안에서부터 바깥으로 차례로 검색해 나가는 것

(현재 스코프) → (바깥쪽 스코프) 

* 전역 스코프까지 반복

 

outerEnvironmentReference

스코프 체인을 구현하는 내부 메커니즘

각 실행 컨텍스트는 outerEnvironmentReference 를 가지며, 이는 현재 호출된 함수가 선언될 당시의 LE 를 참조한다.

* 선언될 당시 : 콜 스택 상에서 실행 컨텍스트가 활성화 된 상태

 

이를 통해 내부 함수는 외부 함수의 변수에 접근할 수 있다. (외부에서 내부는 접근 X)

 

* 변수 은닉화

내부 스코프에서 외부 스코프와 동일한 이름의 변수를 선언하면, 내부 변수가 외부 변수를 '가리게' 되는데 이때 내부에서 외부 변수에 직접 접근할 수 없다.

이때 전역 변수에 접근하기 위해서는 window 객체(브라우저) or global 객체(Node.js) 를 통해 접근이 가능하다.

'자바스크립트' 카테고리의 다른 글

콜백함수  (0) 2024.12.14
this  (0) 2024.12.13
얕은복사와 깊은복사, structuredClone()  (0) 2024.12.02
스택(Stack)과 힙(Heap)  (0) 2024.12.02
데이터 타입  (0) 2024.11.26