\(@^0^@)/

[BOOK] 코어 자바스크립트, 실행 컨텍스트 본문

BOOKS/코어 자바스크립트

[BOOK] 코어 자바스크립트, 실행 컨텍스트

minjuuu 2022. 3. 4. 18:07
728x90

오늘 읽은 범위 : (실행 컨텍스트) p.36 ~ p.53 



< 책에서 기억하고 싶은 내용 >

실행 컨텍스트

  • 실행 컨텍스트 : 실행할 코드에 제공할 환경 정보들을 모아놓은 객체
    • 스택 (stack) : 출입구가 하나뿐인 깊은 우물 같은 데이터 구조
    • 큐 (queue) : 양쪽이 모두 열려있는 파이프, 보통 한쪽은 입력만 다른 한쪽은 출력만을 담당.
    • 실행 방법 : 동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이를 call stack에 쌓아 올렸다가 실행.
    • 구성 방법 : 함수를 실행하는 것 (자동으로 생성되는 전역 공간과 eval 제외)

1. JS 파일이 실행되는 순간 전역 컨텍스트가 활성화되어 콜 스택에 담김.
2. outer 함수를 호출하는 순간 해당 함수의 환경 정보를 수집해서 실행 컨텍스트를 생성한 후 콜 스택에 담음.
3. 그 후 outer 함수 내부의 코드들을 순차로 실행.
4. outer 함수 내부에서 inner 함수를 호출할 때도 마찬가지.
( 함수 호출 -> 환경 정보 수집 -> 콜 스택 저장 -> 내부 코드 순차적 실행 )


실행 컨텍스트의 수집 정보


Variable Environment에 정보를 먼저 담고,
이를 그대로 복사해서 Lexical Environment를 만들고 이후 Lexical Environment를 주로 사용

  • Variable Environment
    • environmentRecord (snapshot) : 현재 컨텍스트 내의 식별자들에 대한 정보
    • outerEnvironmentReference (snapshot) : 외부 환경 정보
  • Lexical Environment (VariableEnviroment와 같지만 변경 사항이 실시간으로 반영됨)
    • environmentRecord : 현재 컨텍스트 내의 식별자들에 대한 정보
    • outerEnvironmentReference : 외부 환경 정보
  • ThisBinding : 식별자가 바라봐야 할 대상 객체

environmentRecord

  • 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집
  • 매개변수의 이름, 함수 선언, 변수명 등이 담김

호이스팅 (hoisting)

변수 정보를 수집하는 과정을 더욱 이해하기 쉬운 방법으로 대체한 가상의 개념으로,
어떤 실행 컨텍스트가 활성화되는 시점에 선언된 변수를 위로 끌어올리고 (호이스팅),
외부 환경 정보를 구성하고, this 값을 설정하는 등의 동작을 수행

등장 배경 : 변수 정보를 수집하는 과정을 모두 마쳤더라도 아직 실행 컨텍스트가 관여할 코드들은 실행되기 전임에도 불구하고 JS엔진은 이미 해당 환경 코드의 변수명들을 모두 알고 있음. (environmentRecord에 저장해서)
그렇기에, 편의상 'JS엔진은 식별자들을 최상단으로 끌어올려놓은 다음 실제 코드를 실행한다'라고 간주하기 위해 등장.

// 함수 선언의 호이스팅 1 - 원본 코드
function a () {
    console.log(b);			// (1)
    var b = 'bbb';			// 수집 대상 1 (변수 선언)
    console.log(b);			// (2)
    function b () { }			// 수집 대상 2 (함수 선언)
    console.log(b);			// (3)
}
a();


// 함수 선언의 호이스팅 2 - 호이스팅을 마친 상태
function a () {
    var b;                          // 수집 대상 1. 변수는 선언부만 끌어올린다.
    var b = function b () { }       // 수집 대상 2. 함수 선언은 전체를 끌어올린다.
                                    // 함수 선언문을 함수 표현식으로 변경
    
    console.log(b);                 // (1)
    b = 'bbb';                      // 변수의 할당부는 원래 자리에 남겨둔다.
    console.log(b);                 // (2)
    console.log(b);                 // (3)
}
a();

< 함수 선언의 호이스팅 2 >
1. 변수 b 선언, 메모리에서 저장할 공간 미리 확보하고 확보한 공간의 주솟값을 변수 b에 연결
2. 위에 선언된 변수 b가 있으므로 선언 과정 무시, 함수는 별도의 메모리에 저장되고 주솟값을 b와 연결, 변수 b는 함수
3. 변수 b에 할당된 함수 b 출력 : ƒ b () { }
4. 변수 b에 'bbb'를 할당. 함수와 연결된 주솟값을 'bbb'의 주솟값으로 덮어씀. 변수 b는 'bbb'
5. (2)와 (3) 모두 'bbb'가 출력됨.
6. 함수 내부의 모든 코드가 실행됐으므로 실행 컨텍스트가 콜 스택에서 제거됨.


함수 선언문과 함수 표현식

  • 함수 선언문 : function 정의부만 존재하고 별도의 할당 명령이 없는 것, 반드시 함수명이 정의되어 있어야 함.
  • 함수 표현식 : 정의한 function을 별도의 변수에 할당하는 것, 함수명이 정의되어 있지 않아도 됨.
    • 기명 함수 표현식 : 함수명을 정의한 표현식, 외부에서는 함수명으로 함수를 호출할 수 없고, 내부에서만 접근 가능.
    • 익명 함수 표현식 : 함수명을 정의하지 않은 표현식. 일반적으로 사용
// 함수 선언문, 함수명 a가 곧 변수명
function a() { }
a();

// 익명 함수 표현식, 변수명 b가 곧 함수명.
var b = function () { }
b();

// 기명 함수 표현식, 변수명은 c, 함수명은 d.
var c = function d () { }
c(); // 실행 ok
d(); // error

함수 표현식을 사용해야 하는 이유, 더욱 안전한 이유

  • 함수 선언문 : 내부 코드까지 전체를 호이스팅
    함수 표현식 : 할당의 과정은 그대로 두고 
    선언부만 호이스팅
    (함수도 하나의 값으로 취급하고, 함수를 다른 변수에 값으로써 '할당' 한 것)
  • 함수 선언문은 선언 전에 호출해도 오류 없이 실행된다.
    오류가 없기에 좀 더 쉽게 접근할 수 있게 해 준다고 생각할 수도 있지만, 큰 혼란을 일으키는 원인이 됨.
  • 함수 표현식을 사용한다면, 선언 전에 함수를 호출하는 코드가 있었다면 에러가 검출되므로 디버깅도 쉽다.

< 오늘 읽은 소감? 떠오르는 생각을 가볍게 적어보기 >

매개변수와 변수에 대한 호이스팅은 이해하고 있었는데, 함수 선언의 호이스팅은 오늘에서야 제대로 알게됐다.
함수 선언문과 함수 표현식의 차이도 모르고 사용했다는 것에 대해 또 한번 반성하는 하루..
나는 보통 const a = ( ) => { } 의 함수식을 사용해왔어서 그래도 제대로 된 함수를 사용했다는 것에 안도했음!
근데 함수 선언문도 은근 사용하긴 했던 것 같은데, 보통 함수를 utils나 hooks로 뺄 때 사용했던 것 같다.
이제부터는 무조건 표현식으로 사용하고, 코드를 한줄 한줄 적을 때 마다 그냥 무작정 구현하는거에만 초점을 두지말고, 전체적인 구조를 생각하면서 조금 더 '개발자'답게 코딩하는 연습을 해야겠다.
나무를 보지말고 숲을 보아라!



< 소감 3줄 요약 >

  • 함수 호출 -> 환경 정보 수집 -> 콜 스택 저장 -> 내부 코드 순차적 실행
  • 호이스팅 : 어떤 실행 컨텍스트가 활성화되는 시점에 선언된 변수를 위로 끌어올리고 (호이스팅),
    외부 환경 정보를 구성하고, this 값을 설정하는 등의 동작을 수행
  • 함수 선언문보다 함수 표현식이 더 안전하다.  (표현식은 선언 후 호출해야 하며, 그렇지 않으면 오류 발생)



[ 출처 : 코어 자바스크립트 ]

728x90