본문 바로가기
개발 공부 일지/JavaScript

[코어 자바스크립] 실행 컨텍스트 / 호이스팅

by yelimu 2025. 2. 19.

실행컨텍스트

  • 실행할 코드에 제공할 '환경 정보'를 모아둔 객체
  • 이것을 콜스택에 쌓아 올렸다가, 가장 위의 컨텍스트와 관련있는 환경 정보를 수집 후, 코드들을 실행
  • 종류
    • 전역 실행 컨텍스트 : 자동 생성
    • 함수 실행 컨텍스트 : 우리가 흔히 실행 컨텍스트를 구성하는 방법 = 함수 호출 = 함수 실행 (함수 선언X)
    • eval 실행 컨텍스트
      콜스택 = 실행 대상임

각각의 실행 컨텍스트에 담기는 정보

  • VariableEnvironment : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보 , 선언 시검의 LexicalEnvironment의 스냅샷(변경사항 반영X)
    • environmentRecord : 정보 수집 -> 호이스팅
    • outerEnvironmentReference : 스코프 체인
  • LexicalEnvironment : VariableEnvironment 를 복사 -> 변경사항 반영ㅇ
  • ThisBinding : this 식별자가 바라봐야할 대상 객체

LexicalEnvironment

(어휘적) 사전적인 환경 : 컨텍스트를 구성하는 환경 정보를 모아둠

environmentRecord: 현재 컨텍스트와 관련된 코드의 식별자 정보 저장

 

식별자 정보

  • 함수에 지정된 매개변수 식별자
  • 선언한 함수 그 자체
  • var로 선언한 변수의 식별자

=> 코드가 실행되기 전에 JS엔진은 이미 해당 환경에 속한 코드의 변수명을 모두 알고 있다.
<=> 식별자를 모두 최상단으로 끌어올려놓고나서 실제 코드를 실행한다고 편의상 개념을 도입

호이스팅 (식별자만ㅇ, 어떤 값을 할당했는지는 관심X)

▶️ 호이스팅이란 실행컨텍스트의 Lexical Envirionment 의 environmentRecord 가 함수 실행 전에 식별자 정보를 수집하는 과정을 추상화한 개념으로, 실행 컨텍스트가 관여하는 코드 집단의 최상단으로 식별자를 끌어올린다고 해석함 

 

function a (){
    console.log(b); // --- (1) 에러 또는 undefined가 출력될거 같지만? 
    var b = 'bbb';
    console.log(b); // --- (2)
    function b (){}
    console.log(b) // --- (3)

}

▼ 호이스팅

function a (){
    var b; // 호이스팅 (변수는 선언부만)
    function b (){ // 함수 선언 전체 호이스팅

    };
    console.log(b); // --- (1) => function b(){}
    b = 'bbb';
    console.log(b); // --- (2) => 'bbb'
    console.log(b); // --- (3)
}

함수 선언문은 함수명으로 선언한 변수에 함수를 할당한 것 처럼 여길 수 있다.

function a (){
    var b; 
    var b = function b (){}; // 변수에 함수를 할당
    console.log(b); // --- (1) => function b(){}
    b = 'bbb';
    console.log(b); // --- (2) => 'bbb'
    console.log(b); // --- (3) => 'bbb'
}

함수 선언문 vs 표현식

// 선언문
function a (){ ... }

// 표현식 
// 익명 함수 표현식
var b = function (){ ... } // b()로 함수 호출 
// 기명 함수 표현식
var c = function d(){ ... } //c()로 함수 호출, d()는 함수 내부에서만 호출ㅇ. 외부에서는 호출X

 

예제 )

console.log(sum(1,2));
console.log(mutiply(3,4));

function sum (a,b){ //선언문
    return a+b;
}

var multiply = function (a,b){ //표현식
    return a*b;
}

 

▼호이스팅

function sum (a,b){ //선언문 : 전체 호이스팅
    return a+b;
}
var multiply; // 표현식 : 선언부만 호이스팅

console.log(sum(1,2)); // 3 
console.log(mutiply(3,4)); // not a function 에러 : 런타임 종료 


multiply = function (a,b){ //표현식
    return a*b;
}

표현식: 함수를 하나의 값으로 취급할 수 있다.

 

상대적으로 표현식이 안전하다?
동명의 함수가 함수 선언식으로 되어있을때

console.log(sum(3, 4))
function sum (x, y){
    return x + y;
}
var a = sum(1,2); 

// -------- 나중에 추가된 코드
function sum (x, y){ 
    return x + ' + ' + y + ' = ' + (x+y);
}
var c = sum(1, 2)
console.log(c)

함수 선언식은 전체가 호이스팅 되므로 추가된 코드 위에 작성된 코드도 모두 나중에 선언된 sum으로 실행됨


스코프, 스코프 체인, outerEnvironmentReference

스코프 = 식별자의 유효 범위

  • 전역 스코프
  • 함수 스코프
  • 블록 스코프 (ES6 ~)

스코프 체인 : 식별자의 유효 범위(스코프)를 안에서부터 바깥쪽으로 검색해 나가는 것

A 외부에서 선언한 변수는 A 내부에서 접근 가능하지만,
A 내부에서 선언한 변수는 오직 A 내부에서만 접근할 수 있고 A 외부에서 접근할 수 없다.

: 함수 자신이 선언될 당시의 LexicalEnvironment를 참조한다 (가장 가까운 요소부터 차례로 접근)

=> 여러 스코프에서 동일한 식별자를 선언한 경우 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자만 접근 가능

=> outerEnvironmentReference 가 있기에 가능함


*선언 = 어떤 실행 컨텍스트가 활성화된 상태에서 일어남 

var a = 1;
function outer(){
    function inner(){
    console.log(a);
    var a = 3;
    }
    inner();
    console.log(a);
}
outer();
console.log(a);
코드 전역 컨텍스트 outer 실행 컨텍스트 inner 실행 컨텍스트 outer 전역
var a = 1; 전역 컨텍스트가 콜스택에 담긴다. (활성화)
(브라우저에서 자바스크립트 파일이 열리는 순간)
       
function outer(){          
function inner(){          
console.log(a);     a = undefined 출력
(변수는 호이스팅 되지만 값이 할당되기 전)
   
var a = 3;     a 할당 / inner 컨텍스트 종료
(이 코드가 없다면 outer 함수의 LexicalEnvirionment 참조 => a = 1출력)
   
inner();   inner함수 실행
=> inner 함수 실행 컨텍스트 생성/활성화
     
console.log(a);       a = 1 출력
outer 선언 당시
 
outer(); outer함수 실행
=> outer 함수 실행 컨텍스트 생성 (outer에 대한 환경정보 수집)
       
console.log(a);         a = 1출력
LexicalEnvironment 

전역 컨텍스트 L.E
environmentRecord : a, outer

outer 컨텍스트 L.E
environmentRecord : inner
outerEnvironmentReference : Global L.E 를 참조

inner 컨텍스트 L.E
environmentRecord : a
outerEnvironmentReference : outer L.E 를 참조
  • inner -> outer -> 전역 컨텍스트 : 스코프 체인을 타면서 접근 가능한 변수의 수가 늘어남
  • inner 안에 a 가 선언되어 외부의 a에는 접근 불가함 (은닉)
    스코프 체인 상의 모든 변수에 접근 가능한것은 아님. 스코프 체인 상 첫번째 인자 (inner 스코프의 LE) 부터 검색하고, 거기서 a가 반환되면 더이상 스코프 체인 검색을 진행하지 않음
  • console.dir( ) 로 상위 스코프 정보를 확인 가능함
  • 디버거 이용

전역 변수 vs. 지역 변수

  • 전역 컨텍스트의 LE에 답긴 변수 = 전역 변수
  • 그 밖의 함수에 의해 생성된 실행 컨텍스트의 변수 = 지역 변수

원문 그대로 사용한 용어를 사용하다보니 처음 읽었을때 뭐가 뭐였지 하고 헷갈리기도 하고 이해하기가 조금 어려웠다.

포스팅을 하면서 한번 더 봤더니 조금 익숙해진것같다.

호이스팅이 단순히 코드를 위로 끌어올리는 것이라고만 알고있었는데, 자바스크립트 엔진의 변수와 함수를 처리하는 방법과 주체 등의 동작 원리를 이해할 수 있었다.

 

모의 면접 질문에 대한 답을 다시 해보자


- 중복선언

var 중복선언 가능, let,const 중복선언 안됨. var 중복선언하면 오버라이드 되어 이전 값은 덮어씌워진다.

 

- 스코프? var는 전역스코프, let,const 블록 스코프 => if, for, while 같은 블록 안에서 선언될 경우 블록 밖에서 접근 불가

var는 함수 스코프이다.  블록 {}을 무시한다. 

선언된 함수 내에서만 접근할 수 있다. 함수 바깥에서 선언하면 전역 스코프이다. 

 

 

- var변수는 어디에서 접근 가능한가? 호이스팅이 일어나서 undefined가 할당되서 접근 가능하다. 

선언된 컨텍스트 내부, 또는 그 내부의 함수 스코프에서도 접근이 가능하다. (동명의 변수명을 갖지 않는다면) 

 

- 호이스팅은 모두 일어난다. let,const 선언하기 전에 변수에 접근을 하면 에러가 발생함.잘 모르지만, 선언과 할당에 시점이 차이가 있다. 

변수 선언부만 호이스팅 됨. 할당은 실제 코드에서 일어남.

 

- 호이스팅의 정의란? 마치 맨위에 코드가 작성된 것 처럼 선언된 것 처럼 끌여올려진 것 

 

- 호이스팅이 되면 어떤 현상이 일어나는지? var는 선언은 되지만 할당이 되지 않았기 때문에 값에 접근하려고하면 undefined를 반환한다. 

변수의 선언부, 선언식 함수 전체가 맨 위로 끌여올려진다.

 

- 호이스팅이 일어나는 원인? 잘 모르겠다. 

실행 컨텍스트의 LexicalEnvironment 의 environmentRecord가 식별자(변수, 함수) 정보를 수집하기 때문에 함수를 실행하기 전부터 정보를 알고 있다. 

 

- 실제로 코드가 위로 올라가는지? 변수선언을 먼저 해석하기 때문에 실제로 코드가 위로 올라간다고 할 수 있을 것 같다.

실제로 코드가 위로 올라가는것은 아님. 자바스크립트 엔진이 선언 부분을 미리 처리하는 방식


이 포스팅은 <코어 자바스크립트> 를 읽고 내용을 정리한 포스팅입니다.

틀린 내용이 있다면 알려주세요 _ _