JS 실행컨텍스트 & 스코프

이수빈·2024년 1월 13일
0

Html, Css, JS

목록 보기
6/7
post-thumbnail

스코프

  • 식별자가 유효한 범위

스코프는 다음과 같이 구분한다.

  • 전역 스코프 (Global scope) => 전역변수
  • 지역 스코프 (Local scope or Function-level scope) => 지역변수
  • JS는 렉시컬 스코프 따름 => 함수를 어디서 호출하는지가 아니라 어디서 선언하는지에 따라 결정됨.(엔진이 코드실행전 평가과정을 거치기 때문)

  • 정의시점에 상위스코프를 렉시컬환경에 상위스코프에 대한 참조에 기록함

var x = 1;

function foo() {
  var x = 10;
  bar();
}

function bar() {
  console.log(x);
}

foo(); // 1
bar(); // 1
  • 실행컨텍스트의 렉시컬 환경임.

  • 렉시컬 환경 > 환경레코드(스코프에 대한 변수값..), 외부 렉시컬 환경에 대한 참조(상위스코프)

  • 스코프체인 : 스코프가 계층적으로 연결된 구조임.

  • 변수를 참조할 때 JS 엔진은 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하며 선언된 변수를 검색한다.

  • 블록레벨 스코프 : if, for ,while…등 블록이 하나의 지역 scope를 만드는것

  • 함수레벨 스코프 : 오직 함수의 코드블록(함수몸체) 만을 지역 scope로 인정하는 것.

  • var는 함수레벨스코프만, let,const는 블록레벨 스코프도 지원한다.

  • 렉시컬 스코프(정적스코프) : 함수를 어디서 정의했는지에 따라 함수의 상위 스코프를 결정한다. ↔ 동적스코프

  • bar은 전역에서 정의된 함수 > 어디에서 호출되었는지 상관없이 자신이 기억하고 있는 전역스코프를 상위스코프로 사용한다

var x =1;

function foo(){
	var x = 10;
	bar();
}

function bar(){
	console.log(x);
}

foo();// 1  10이 아닌이유 > bar은 전역스코프인 함수이기 때문임.
bar();// 1 

실행컨텍스트

  • 실행 컨텍스트는 소스코드를 실행하는데 필요한 환경을 제공하고 코드의 실행결과를 실제로 관리하는 영역이다.

  • 실행 컨텍스트에는 LexicalEnvironment 컴포넌트와 VariableEnvironment 컴포넌트가 있고, 렉시컬 환경에는 EnvironmentRecord, OuterLexicalEnvironmentReference 컴포넌트가 존재함.

  • 보통 VariableEnvironment와 LexicalEnvironment은 달라지는 경우도 존재

  • 식별자와 스코프는 실행컨텍스트의 렉시컬환경으로 관리하고, 코드 실행순서는 실행 컨텍스트 스택으로 관리된다.

  • 실행컨텍스트는 식별자를 등록하고 관리하는 스코프와 실행 순서 관리를 구현한 내부 메커니즘으로, 모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다.

  • 소스코드의 4가지 구분 > 4가지 타입의 소스코드는 실행 컨텍스트를 생성한다.

  • 전역코드 : 전역에 존재하는 소스코드.
  • 함수코드 : 함수 내부에 존재하는 소스코드.
  • eval코드 : eval 함수에 인자로 전달되어 실행되는 소스코드
  • 모듈코드 : 모듈 내부에 존재하는 소스코드.

엔진의 소스코드 평가과정

  • JS는 소스코드를 소스코드 평가와 소스코드 실행 2가지 과정으로 나누어서 처리한다.
  • 평가과정 : 실행컨텍스트 생성, 선언문만 실행 > 스코프에 등록함
  • 실행과정 : 선언문을 제외한 소스코드가 순차적으로 실행되기 시작함.

실행컨텍스트 스택

  • 실행 컨텍스트 스택 : 생성된 실행 컨텍스트가 스택으로 관리되는것. (최상위가 실행됨.)
const x = 1;

function foo(){
    const y = 2;
    function bar(){
        const z = 3;
        console.log(x+y+z);
    }
    bar();
}

foo();//6

/*실행 컨텍스트 순서
1. 전역 컨텍스트 생성 > 전역 변수 x, 전역 함수 foo를 등록함.
2. 전역코드 실행 > foo() 호출
3. 함수 실행컨텍스트 생성 > 지역변수 y, 중첩함수 bar를 함수 실행 컨텍스트에 등록
4. bar함수 실행 컨텍스트 생성하고, 코드 실행후 stack에서 pop
5. 나머지 pop과정 반복

실행컨텍스트 구성

  • 실행컨텍스트는 다음과 같이 3가지로 구성된다.

VariableEnvironment
현재 컨텍스트 내의 식별자(변수)들에 대한 정보
외부 환경 정보
선언 시점의 LexicalEnvironment의 스냅샷(변경사항 반영 X)
LexicalEnvironment
처음에는 VariableEnvironment와 같음
변경 사항이 실시간으로 반영됨
ThisBinding
식별자가 바라봐야 할 대상 객체

  • 렉시컬 환경 : 식별자와 식별자에 바인딩된 값, 상위스코프에 대한 참조를 기록하는 자료구조로 실행 컨텍스트를 구성하는 컴포넌트이다. > 스코프와 식별자를 관리한다.

  • 렉시컬 환경의 구성

    • 환경레코드(Environment Record) : 스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩된 값을 관리하는 저장소
    • 객체환경 레코드 : var로 선언한 전역변수, 선언문으로 정의한 전역함수, 빌트인 전역 프로퍼티, 함수..
    • 선언적 환경 레코드 : let const키워드로 선언한 전역변수.
    • 외부 렉시컬 환경에 대한 참조 : 상위 스코프를 가르킨다. 해당 실행 컨텍스트를 생성한 소스코드를 포함하는 상위코드의 렉시컬 환경을 말한다.

var x = 1;
const y = 2;

function foo(a){
    var x = 3;
    const y = 4;

    function bar(b){
        const z = 5;
        console.log(a+b+x+y+z);
				//20+10+3+4+5
    } //렉시컬스코프 > 상위스코프 참조

    bar(10);
}

foo(20);//42

/* 전역코드 평가
1.전역 실행 컨텍스트 생성
2.전역 렉시컬 환경 생성
 2.1 전역 환경 레코드 생성
		2.1.1. 객체 환경 레코드 생성
		2.1.2. 선언적 환경 레코드 생성
	2.2 this 바인딩 //this값이 어떤값을 가르키냐를 결정함.
	2.3 외부 렉시컬 환경에 대한 참조 결정. // 상위스코프 기억함.
*/

/* 함수코드 평가
1.함수 실행 컨텍스트 생성
2.함수 렉시컬 환경 생성
  2.1 함수 환경 레코드 생성
	2.2 this 바인딩 //this값이 어떤값을 가르키냐를 결정함.
	2.3 외부 렉시컬 환경에 대한 참조 결정. // 상위스코프 기억함.
*/
  • js엔진은 코드를 실행하면 => 전역실행컨텍스트를 생성해 콜스택에 넣음

왜 실행컨텍스트에서는 Variable Environment와 Lexical Environment를 분리해 놓은 것일까?

(Ecma 공식스펙)

  • block scope 관련 Code Evalution에서 차이가 존재한다.

  • let, const는 기본적으로 블록스코프를 따르고,

  • var은 기본적으로 함수스코프를 따른다. (블록스코프 지원 x)

  • 블록스코프가 코드영역에서 평가될 때 새로운 Execution Context를 생성하는 것이 아니라, block Scope만의 별도 Lexical Enrvionment를 생성한다.

  • 상위 EC가 기존의 Lexical Envrionment 대신 블록스코프의 Lexical Envrionment를 참조했다가, 기존의 종료된 시점에 원래의 Lexical Environment를 복원하는 방식으로 실행됨.

  • 아래와 같은 코드가 있다고 가정

function Hi(){
    let let1 = 'I am let1';
    var var1 = 'I am var1';
    if(true){
         let let2 = 'I am let2';
         var var2 = 'I am var2';
    }
}

  • 이런식으로 블록에 대한 Lexical Environment가 생성되어 outer에 참조값을 기록하는 형식으로 코드가 실행됨.

  • var로 선언한 변수는 함수레벨 스코프를 따름 => Variable Envrionment의 렉시컬 Envrioment에 환경변수에 저장됨.

  • 또한 Variable Environment와 Lexical Environment 동작방식의 차이가 존재한다.

실제로 Execution Context의 생성 과정은 크게 두 페이즈로 나누어진다.
1. Creation Phase(Execution Context에 대한 정의 과정)​
2. Execution Phase(코드를 실행하는 과정)

  • Variable Environment에서는 var로 선언된 변수를 변수 선언 1, 2단계(Declaration phase, Initialization phase) 모두 진행하기에 메모리에 매핑되고 undefined로 초기화까지 마치게 된다.(선언, 초기화 동시실행)

  • 반면에 Lexical Environment의 경우는 let, const로 선언된 변수를 1단계(Declaration phase)만 진행하기에 Variable Environment와는 동작 방식에 차이가 있다.(선언, 초기화 분리)

ref) variable vs lexical environment : https://m.blog.naver.com/dlaxodud2388/222655214381

https://stackoverflow.com/questions/23948198/variable-environment-vs-lexical-environment

ref) 포에마웹
모던 자바스크립트 딥다이브 강의 : https://www.youtube.com/watch?v=NKEOFnMtBh8

profile
응애 나 애기 개발자

0개의 댓글