실행 컨텍스트

RHUK2·2021년 5월 3일
0

Javascript

목록 보기
48/56

📚 Reference


Mina's World, https://mingcoder.me/2020/02/28/Programming/JavaScript/execute-context/
PoiemaWeb, https://poiemaweb.com/es6-block-scope
PoiemaWeb, https://poiemaweb.com/js-function

참고 사이트에 내용을 개인적으로 복습하기 편하도록 재구성한 글입니다.
자세한 설명은 참고 사이트를 살펴보시기 바랍니다.


서론


자바스크립트 언어를 이해하는데 가장 중요하고 기본적인 컨셉은 실행 컨텍스트를 이해하는 것입니다. 이를 잘 이해한다면 호이스팅, 스코프 체인, 클로저를 이해하기 쉬울 것입니다.

실행 컨텍스트라는 것이 무엇인지 이해하기 위해 소프트웨어를 어떻게 만드는 지 생각해봅시다. 소프트웨어를 작성하는 기본적인 전략은 코드를 여러 조각으로 나누는 것입니다. 코드 조각들은 functions, modules, packages 등으로 나누어집니다. 이렇게 분리하는 이유는 소프트웨어의 복잡성을 줄이고 관리하기 쉽게 하기 위함입니다.

소프트웨어를 작성하는 입장을 이해하고 코드를 해석하는 자바스크립트 엔진 관점에서 생각해봅시다. 자바스크립트 엔진은 코드 해석의 복잡성을 관리하기 위해서 소프트웨어를 작성하는 기본 전략처럼 코드를 여러 조각으로 나눕니다. 이렇게 나누어진 조각들을 실행 컨텍스트라고 부릅니다.


실행 컨텍스트


  • 실행 컨텍스트의 종류는 두 가지입니다.

    • 글로벌 실행 컨텍스트

    • 함수 실행 컨텍스트

  • 실행 컨텍스트는 두 단계로 실행됩니다.

    • 생성 단계

    • 실행 단계


글로벌 실행 컨텍스트의 생성과 실행


글로벌 실행 컨텍스트

  • 자바스크립트 엔진이 코드를 해석 시 가장 먼저 생성되는 실행 컨텍스트입니다.

  • 글로벌 실행 컨텍스트는 기본적으로 두 가지로 구성됩니다.

    • global object

    • this

  • 아무런 코드를 작성하지 않아도 글로벌 실행 컨텍스트는 위 두 가지 구성 요소를 포함합니다.

생성 단계

글로벌 실행 컨텍스트 생성 단계

  • 현재 단계는 자바스크립트 엔진이 코드를 한 줄씩 읽고 실행하지 않습니다.

  • 자바스크립트 엔진은 다음과 같이 동작합니다.

    • global object 생성

    • this 생성

    • var 변수를 글로벌 실행 컨텍스트에 선언하고 메모리 공간을 확보하여 undefined로 초기화

    • 선언식 함수를 선언하고 메모리 공간을 확보하여 함수를 할당

실행 단계

글로벌 실행 컨텍스트 실행 단계

  • 현재 단계에 접어들면, 자바스크립트 엔진은 코드를 한 줄씩 실행

  • 코드를 한 줄씩 실행하면서 var변수에 값을 할당

생성 단계와 실행 단계 동작 과정

글로벌 실행 컨텍스트 동작 과정


함수 실행 컨텍스트의 생성과 실행


함수 실행 컨텍스트

  • 함수 실행 컨텍스트는 함수가 호출될 때마다 생성되는 실행 컨텍스트입니다.

  • 함수 실행 컨텍스트는 기본적으로 두 가지로 구성됩니다.

    • arguments object

    • this

  • 함수 내부에 아무런 코드를 작성하지 않아도 함수 실행 컨텍스트는 위 두 가지 구성 요소를 포함합니다.

생성 단계

  • 함수가 호출되면 함수 실행 컨텍스트를 생성합니다.

  • 자바스크립트 엔진은 다음과 같이 동작합니다.

    • arguments object 생성

    • this 생성

    • var 변수를 함수 실행 컨텍스트에 선언하고 메모리 공간을 확보하여 undefined로 초기화

    • 선언식 함수를 선언하고 메모리 공간을 확보하여 함수를 할당

  • 글로벌 실행 컨텍스트와 차이점은 global object가 아닌 arguments object를 생성한다는 것이고 나머지는 동일합니다.

실행 단계

  • 실행 단계에 접어들면, 자바스크립트 엔진은 함수 내부에 코드를 한 줄씩 실행

  • 코드를 한 줄씩 실행하면서 var변수에 값을 할당

생성 단계와 실행 단계 동작 과정

파라미터와 지역 변수가 없을 경우

함수 실행 컨텍스트 동작 과정1

  • 위 동작 과정 중에는 함수 내부에 var 변수와 선언식 함수가 없기에 메모리 할당 과정을 거치지 않습니다.

파라미터와 지역 변수가 있는 경우

함수 실행 컨텍스트 동작 과정2

  • getURL() 함수는 handle을 인자로 넘겨 받고, 함수 내부에 var 변수를 가집니다.

  • 생성 단계에서 인자로 전달된 handlearguments object에 추가하고, 함수 실행 컨텍스트 내부에 지역 변수로 추가됩니다.

  • 지역 변수 twitterURL은 생성 단계에서 메모리 공간을 확보하고 undefined로 초기화됩니다.

  • 이후 실행 단계에서 twitterURL은 값을 할당받습니다.

함수 실행 컨텍스트가 Call Stack에 쌓이는 과정

함수 실행 컨텍스트가 Call Stack에 쌓이는 과정

  • 자바스크립트 엔진은 싱글 스레드이므로 함수가 호출될 때마다 Call Stack에 함수 실행 컨텍스트를 생성하여 차곡 차곡 쌓습니다.

  • 함수가 호출되면 Call Stack에 쌓였다가 실행을 마치면 Call Stack에서 제거됩니다.


호이스팅


호이스팅(Hoisting)은 실행 컨텍스트의 생성 단계에서 변수와 함수가 동작하는 방식을 의미합니다.

변수의 호이스팅

변수의 호이스팅 설명 이전에, 변수의 생성 과정은 아래와 같이 3단계로 구성됩니다.

선언 단계(Declaration phase)
변수를 실행 컨텍스트에 등록한다.

초기화 단계(Initialization phase)
변수를 위한 메모리 공간을 확보합니다. 이 단계에서 변수는 undefined로 초기화됩니다.

할당 단계(Assignment phase)
undefined로 초기화된 변수에 실제 값을 할당합니다.

실행 컨텍스트의 생성 단계에서 아래와 같이 var를 명시한 이유는 letconst와 호이스팅 방식이 다르기 때문입니다.

  • var 변수를 함수 실행 컨텍스트에 선언하고 메모리 공간을 확보하여 undefined로 초기화

var는 실행 컨텍스트의 생성 단계에서 선언 단계와 초기화 단계가 동시에 일어나고, 실행 단계에서 할당 단계가 실행됩니다.

console.log(foo); // undefined
var foo = 1;
console.log(foo); // 1

letconst는 실행 컨텍스트의 생성 단계에서 선언 단계만 일어나고, 실행 단계에서 초기화 단계와 할당 단계가 실행됩니다.

console.log(foo); // ReferenceError: foo is not defined
let foo = 1;
console.log(foo); // 1

함수 호이스팅

함수를 생성하는 방법은 대표적으로 선언식 함수와 표현식 함수가 있습니다.

// 선언식 함수
function foo() {...};
    
// 표현식 함수
var foo = function() {...};

실행 컨텍스트의 생성 단계에서 아래와 같이 선언식 함수를 명시한 이유는 표현식 함수와 호이스팅 방식이 다르기 때문입니다.

  • 선언식 함수를 선언하고 메모리 공간을 확보하여 함수를 할당

선언식 함수는 실행 컨텍스트의 생성 단계에서 선언과 동시에 메모리 공간을 확보하여 함수를 할당합니다.

foo(); // 함수 호출 가능

function foo() {...};

표현식 함수는 실행 컨텍스트의 생성 단계에서 선언만 이루어지고, 실행 단계에서 메모리 공간을 확보하여 함수를 할당합니다.

foo(); // TypeError: foo is not a function

var foo = function() {...};

스코프 체인


스코프 체인(Scope Chain)이란 현재 실행 컨텍스트에 필요한 변수가 없는 경우, 자바스크립트 엔진이 부모 실행 컨텍스트를 하나씩 차례대로 확인하는 프로세스를 의미합니다. MDN에서는 스코프를 현재 실행 컨텍스트라고 정의합니다.

아래 코드의 동작을 한번 살펴봅시다.

var name = 'Tyler';

function logName() {
  console.log(name)
};

logName(); // Tyler

logName() 함수의 실행 컨텍스트에는 name 변수가 없으므로 not defined 에러가 발생할 것이라고 예상할 수 있습니다. 하지만 자바스크립트 엔진은 함수 실행 컨텍스트에서 지역 변수를 찾지 못하면 가장 가까운 부모 실행 컨텍스트로 이동하여 해당 변수를 찾게됩니다. 이는 글로벌 실행 컨텍스트에 도달할 때까지 거슬러 올라갑니다. 위의 코드에서는 글로벌 실행 컨텍스트에 name 변수를 발견하여 해당 값을 출력할 수 있습니다.


클로저


함수 내부에서 생성된 변수는 내부에서만 접근 가능하고 함수 실행 컨텍스트가 Call Stack에서 제거되면 그 변수에 접근할 수 없다고 알고있습니다. 하지만 예외적인 경우가 있는데, 이는 함수 안에 중첩 함수가 존재하는 경우입니다.

이 경우에는 부모 함수의 실행 컨텍스트가 실행 스택에서 제거되었더라도 자식 함수는 부모 함수의 실행 컨텍스트에 여전히 접근 가능합니다.
클로저 스코프
위의 과정을 확인해보면, makeAdder() 함수의 실행 컨텍스트가 실행 스택에서 제거되고 난 후 Closure Scope를 생성하는 것을 확인할 수 있습니다.

자바스크립트는 makeAdder() 함수 안에 inner() 함수가 존재하고 inner() 함수가 실행되기 전에 반환될 때 makeAdder() 함수의 실행 컨텍스트 환경을 가지게 됩니다.

이런 특징 때문에 makeAdder() 함수가 실행 스택에서 제거되어도 inner() 함수는 생성된 Closure Scope로 변수에 접근할 수 있습니다.

profile
생각 많이 하지 않기 😎

0개의 댓글