til

자바스크립트 집중 학습 4일 차 @실행 컨텍스트, this

fpzmfks 2024. 7. 30. 22:02

오늘은 어제 못다한 코드 정리를 마치고 여태껏 틈틈이 학습한 자바스크립트 강의 내용을 정리하는 시간을 가져보았다. 

 

이번에 살펴볼 자바스크립트 학습 내용은 실행 컨텍스트, this인데 이들은 코드 내에서 변수와 함수 등이 정확히 어떤 순서로 작동하는지들을 살펴보는 데에 매우 유용하다.  여태껏 코드가 작동하지 않았던 원인 중 많은 것들이 변수나 경로를 잘못지정했거나 코드의 알맞은 위치를 잘못 파악했거나 하는 등의 일들이었으니 실행 컨텍스트와 같은 내용을 잘 익혀두면 두고두고 유용하게 쓸 수 있을 것이다. 

 

목차

실행 컨텍스트

This

  • this 바인딩
  •  

    실행 컨텍스트

    먼저 실행 컨텍스트이다. 실행 컨텍스트란 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.

     

    실행 컨텍스트 역할

    1. 코드 순서 보장

    2. 환경 정보 저장

    3. 코드에 필요한 환경 정보 매치

     

    실행 컨텍스트 저장 방식

    출처(https://haileychoi15.medium.com/%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-execution-context-%EC%99%80-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85-hoisting-3f407ad4820e)

     

    컨텍스트가 콜 스택 방식으로 쌓이며 저장되었다가 4단계에서 함수가 실행되며 소비되는 모습을 확인할 수 있다. 

     

    실제 코드의 실행컨텍스트 범위(이미지)

    실행 컨텍스트 구성

    1. VE( VariableEnvironment ) 

    • record(해당 컨텍스트 내의 정보)
    • outer(밖에서 가져오는 정보)
    • snapshot(선언된 정보 고정)

    2. LE( LexicalEnvironment ) @주로 사용되는 부분

    • record
    • outer
    • 실시간 변경 정보

    3. TB( ThisBinding )

    • 식별자가 바라봐야 할 객체(this의 값)

    실제 코드와 실행 컨텍스트(코드)

    전역 실행컨텍스트가 '스코프체인'을 통해 모든 영역에 영향을 끼치고 있음

    // 전역 컨텍스트
    var a = 1;
    function func1 () {
        function func2 () {
            console.log(a); //    1
        }
       func2 (); 
        console.log(a); //    1
        }
    func1 (); 
    console.log(a); //    1
    전역 실행컨텍스트가 '스코프체인'을 통해 모든 영역에 영향을 끼치고 있음
    동시에 func2 컨텍스트가 해당 영역 내에서 영향을 끼치고 있음

    // 전역 컨텍스트
    var a = 1;
    function func1 () {
        function func2 () {
            var a = 3;
            console.log(a); //    3
        }
       func2 (); 
        console.log(a); //    1
        }
    func1 (); 
    console.log(a); //    1
    전역 실행컨텍스트가 '스코프체인'을 통해 모든 영역에 영향을 끼치고 있음
    동시에 func2 컨텍스트가 해당 영역 내에서 영향을 끼치고 있음과
    동시에 func2의 식별자(var a)만 '호이스팅'되어서 console.log(a)에는 값이 할당되지 않음


    // 전역 컨텍스트
    var a = 1;
    function func1 () {
        function func2 () {
            console.log(a); //    undefined
            var a = 3;
        }
       func2 (); 
        console.log(a); //    1
        }
    func1 (); 
    console.log(a); //    1

     

    개별 실행컨텍스트가 활성화될 때 일어나는 일

    1. 호이스팅(선언된 변수를 위로 끌어올림) (레코드의 수집 과정) 

    • 3번째 코드로 예시 
    // func2 호이스팅
    var a;
    console.log(a);  // undefined
    a=3;
    // func2 레코드:식별자 정보 수집(함수에 지정된 매개변수 식별자, 함수 자체, var로 선언된 변수 식별자 등)
    var a;
    // *주의*
    함수 선언문과 함수 표현식은 호이스팅 과정에서 차이가 있다. 
    함수 선언문은 그 자체로 가장 위로 호이스팅 되지만, 함수 표현식은 식별자만 호이스팅 된다. 
    function func1() {};
    let func2;

    func2 = function () {};


    추천하는 건 함수 표현식으로
    협업 등에서 이전 코드에 코드를 삽입할 시 함수 선언문은 호이스팅 되어 해당 실행컨텍스트 전체에 영향을 끼치지만
    함수 표현식은 표현식이 선언된 이후부터 영향을 끼치기 때문이다. 

    2. 아우터 구성(외부환경정보 구성)

    • 1번째 코드로 예시
    // 각 실행컨텍스트 아우터
    전역 실행컨텍스트 아우터 : ??? 아마 글로벌 같은 전역 객체
                                                 (전역 영역에서 a=1이 나오는 건 아우터가 아니라 레코드 때문)
    func1 실행컨텍스트 아우터 : a=1
    func2 실행컨텍스트 아우터 : a=1
    • 2번째 코드로 예시
    // 각 실행컨텍스트 아우터
    전역 실행컨텍스트 아우터 : ??? 아마 글로벌 같은 전역 객체
                                                (전역 영역에서 a=1이 나오는 건 아우터가 아니라 레코드 때문)
    func1 실행컨텍스트 아우터 : a=1
    func2 실행컨텍스트 아우터 : a=1(하지만 레코드var a= 3이 찍혀서 var a가 3으로 갱신되었다.)
    • 아우터보다 레코드가 우선시된다.
    • 좀 더 정확하게 말하자면 스코프 체인이 안에서 밖으로 검색된다.
    • 실행 컨텍스트가 스코프 체인에서 가장 먼저 발견하는 식별자가 레코드이다.

     

    • 코드

    • 스코프 체인


     

    THIS

    3. this 바인딩(this 값 설정)

    자바스크립트에서 this는 어디에서나 사용될 수 있다. 하지만 호출 주체를 명시하지 않으면 global 같은 전역 객체를 가리키게 된다. 

    // CASE1 : 함수
    // 호출 주체를 명시할 수 없기 때문에 this는 전역 객체를 의미해요.
    var func = function (x) {
        console.log(this, x);
    };

    func(1);  // Global { ... } 1

    // CASE2 : 메서드
    // 호출 주체를 명시할 수 있기 때문에 this는 해당 객체(obj)를 의미해요.
    // obj는 곧 { method: func }를 의미하죠?

    var obj = {
        method: func,
    };

    obj.method(2); // { method: func } 2

     

    메서드의 내부 함수라도 global과 같은 전역 객체를 가리키게 되므로 호출 주체를 명시하는 것이 중요하다!

    또 즉시 실행 함수 또한 this가 전역 객체를 가리키게 되므로 주의! 

     

    • 단, es6로 인해 업데이트 된 화살표 함수this 바인딩을 하지 않아 스코프 체인을 따라 값을 반환하므로 호출 주체를 명시하지 않은 함수에서도 상위요소를 this로 지정한다.
    • 생성자 함수 또한 this가 전역 객체를 가리키지 않게 할 수 있다.
    • 또 func.bind({x:1}) 같은 것들을 통해 함수 속의 this에 명시적으로 this binding할 수 있다.