[자바스크립트 중고급] 4. Execution Context

Speedwell🍀·2022년 2월 1일
0

실행 콘텍스트

function music(title){
  var musicTitle = title;
};
music("음악");
  • Exeuction Context

    • 함수가 실행되는 영역, 묶음
    • 함수 코드를 실행하고 실행 결과를 저장
    • 스펙상의 사양
  • music("음악")으로 함수를 호출하면

    • 엔진은 실행 콘텍스트를 생성하고
    • 실행 콘텍스트 안으로 이동
  • 실행 콘텍스트 실행 단계

    • 준비 단계
    • 초기화 단계
    • 코드 실행 단계
  • Execution Context 생성 시점

    • 실행 가능한 코드를 만났을 때
  • 실행 가능한 코드 유형

    • 함수 코드
    • 글로벌 코드
    • eval 코드
  • 코드 유형을 본리한 이유

    • 실행 콘텍스트에서 처리 방법과 실행 환경이 다르기 때문
    • 함수 코드: 렉시컬 환경
    • 글로벌 코드: 글로벌 환경
    • eval 코드: 동적 환경

실행 콘텍스트 상태 컴포넌트

실행 콘텍스트(EC): {
  렉시컬 환경 컴포넌트(LEC): { },
  변수 환경 컴포넌트(VEC): { },
  this 바인딩 컴포넌트(TBC): { },
}
  • 실행 콘텍스트 상태를 위한 오브젝트

    • 실행 콘텍스트 안에 생성
  • 상태 컴포넌트 유형

    1. 렉시컬 환경 컴포넌트(LEC): Lexical Environment Component
    2. 변수 환경 컴포넌트(VEC): Variable Environment Component
    3. this 바인딩 컴포넌트(TBC): This Binding Component

1. 렉시컬 환경 컴포넌트

  • 함수와 변수의 식별자 해결을 위한 환경 설정

  • 함수 초기화 단계에서 해석한

    • 함수와 변수를 {name: value} 형태로 저장
    • 이름으로 함수와 변수를 검색할 수 있게 됨
  • 함수 밖의 함수와 변수 참조 환경 설정

    • 함수 밖의 함수와 변수를 사용할 수 있게 됨

렉시컬 환경 컴포넌트 구성

실행 콘텍스트(EC): {
	렉시컬 환경 컴포넌트(LEC): {
    	환경 레코드(ER): {
        	point: 100
		},
        외부 렉시컬 환경 참조(OLER): {
        	title: "책",
            getTitle: function(){}
		}
	}
}
  • 렉시컬 환경 컴포넌트 생성

    • function, with, try-catch에서 생성
  • 컴포넌트 구성

    • 환경 레코드(ER): Environment Record
    • 외부 렉시컬 환경 참조(OLER): Outer Lexical Environment Reference

렉시컬 환경 컴포넌트 설정

  • 환경 레코드에 함수 안의 함수와 변수를 기록

  • 외부 렉시컬 환경 참조에 function 오브젝트의 [[Scope]]를 설정

  • 따라서 함수 안과 밖의 함수와 변수를 사용할 수 있게 됨


환경 레코드 구성

실행 콘텍스트(EC): {
	렉시컬 환경 컴포넌트(LEC) = {
    	   환경 레코드(ER): {
              선언적 환경 레코드(DER): {
              	point: 123
               },
               오브젝트 환경 레코드(OER): {}
           },
           외부 렉시컬 환경 참조(OLEC): {}
	},
    	변수 환경 컴포넌트(VEC): {},
    	this 바인딩 컴포넌트(TBC): {}
}
  • 환경 레코드를 구분하는 이유
    • 기록 대상에 따라 다르기 때문
  • 선언적 환경 레코드
    • DER: Declarative Environment Record
    • function, 변수, catch 문에서 사용
    • 앞 절에서 환경 레코드에 설정한다고 했는데 설명을 위한 것으로 실제로 여기에 설정
  • 오브젝트 환경 레코드
    • OER: Object Environment Record
    • 글로벌 함수와 변수, with 문에서 사용
    • 정적이 아니라 동적이기 때문

글로벌 환경

실행 콘텍스트(EC): {
	글로벌 환경(GE): {
    	   환경 레코드(ER): {
           	오브젝트 환경 레코드: 글로벌 오브젝트
           },
           외부 렉시컬 환경 참조(OLER): null
	}
}
  • Global Environment

    • 글로벌 오브젝트에서 사용
    • 렉시컬 환경 컴포넌트와 형태 같음
  • 동적으로 함수와 변수 바인딩

    • 함수에서 var 키워드를 사용하지 않고 변수를 선언하면 글로벌 오브젝트에 설정되기 때문
      ➡ 이런 이유로 오브젝트 환경 레코드 사용
  • 외부 렉시컬 환경 참조 값은 null


외부 렉시컬 환경 참조

  • 스코프와 실행중인 함수가 Context 형태이므로

    • 스코프의 변수와 함수를 별도의 처리 없이 즉시 사용할 수 있음
  • 실행 콘텍스트에서

    • 함수 안과 밖의 함수, 변수를 사용할 수 있으므로
    • 함수와 변수를 찾기 위해 실행 콘텍스트를 벗어 나지 않아도 됨

2. 변수 환경 컴포넌트

  • 실행 콘텍스트 초기화 단계에서
    • 렉시컬 환경 컴포넌트와 같게 설정
  • 이렇게 하는 이유는?
    • 초기값을 복원할 때 사용하기 위한 것
  • 함수 코드가 실행되면
    • 실행 결과를 렉시컬 환경 컴포넌트에 설정
    • 초기값이 변하게 되므로 이를 유지하기 위한 것

3. this 바인딩 컴포넌트

  • 목적

    • this로, 함수를 호출한 오브젝트의 프로퍼티에 악세스
    • 예: this.propertyName
  • 악세스 메커니즘

    • obj.book() 형태에서
    • this로 obj를 참조할 수 있도록
    • this 바인딩 컴포넌트에 obj 참조를 설정
  • obj의 프로퍼티가 변경되면 동적으로 참조

    • 설정이 아닌 참조이기 때문
var obj = {point: 100};
obj.getPoint = function(){
  return this.point;
};
obj.getPoint();
실행 콘텍스트(EC): {
	렉시컬 환경 컴포넌트(LEC) = {
    	   환경 레코드(ER): {
              선언적 환경 레코드(DER): {},
               오브젝트 환경 레코드(OER): {}
           },
           외부 렉시컬 환경 참조(OLEC): {}
	},
    	변수 환경 컴포넌트(VEC): {},
    	this 바인딩 컴포넌트(TBC): {
           point: 100,
           getPoint: function(){}
        }
}

getPoint() 함수에서 100이 반환되는 과정

준비 단계

  1. 마지막 줄에서 obj.getPont() 함수 호출

  2. 실행 콘텍스트 생성

  3. 3개의 컴포넌트 생성

    • 렉시컬/변수 환경 컴포넌트, this 바인딩 컴포넌트
  4. this 바인딩 컴포넌트에

    • getPoint()에서 this로 obj의 프로퍼티를 사용할 수 있도록 바인딩

초기화 단계

  1. 파라미터, 함수 선언문, 변수 선언 없음

실행 단계

  1. return this.point; 실행

  2. this 바인딩 컴포넌트에서 point 검색

    • getPoint() 함수를 호출한 오브젝트가
    • this 바인딩 컴포넌트에서 설정(참조)된 상태
  3. this 바인딩 컴포넌트에

    • point 프로퍼티가 있으므로 100을 반환

추가 설명

  1. obj.getPoint()에서 obj의 프로퍼티가

    • this 바인딩 컴포넌트에 바인딩되도록 의도적으로 설계해야 함

실행 콘텍스트 실행 과정

var base = 200;
function getPoint(bonus){
  var point = 100;
  return point + base + bonus;
};
log(getPoint(70));

// 370
  1. getPoint 오브젝트의 [[Scope]]에 글로벌 오브젝트 설정

  2. 마지막 줄에서 getPoint() 함수 호출하면

  3. 엔진은 실행 콘텍스를 생성하고 실행 콘텍스트 안으로 이동

준비 단계

  1. 컴포넌트를 생성하여 실행 콘텍스트에 첨부

    • 렉시컬 환경 컴포넌트
    • 변수 환경 컴포넌트
    • this 바인딩 컴포넌트
  2. 환경 레코드를 생성하여 렉시컬 환경 컴포넌트에 첨부

    • 함수 안의 함수, 변수를 바인딩

    <여기까지의 모습>

실행 콘텍스트(EC): {
	렉시컬 환경 컴포넌트(LEC) = {
    	   환경 레코드(ER): {}
	},
    	변수 환경 컴포넌트(VEC): {},
    	this 바인딩 컴포넌트(TBC): {}
}
  1. 외부 렉시컬 환경 참조를 생성하여 렉시컬 환경 컴포넌트에 첨부하고

    • function 오브젝트의 [[Scope]]를 설정

<여기까지의 모습>

실행 콘텍스트(EC): {
	렉시컬 환경 컴포넌트(LEC) = {
    	   환경 레코드(ER): {},
           외부 렉시컬 환경 참조(OLEC): {
              base: 200
           }
	},
    	변수 환경 컴포넌트(VEC): {},
    	this 바인딩 컴포넌트(TBC): {}
}

초기화 단계

  1. 호출한 함수의 파라미터 값을

    • 호출된 함수의 파라미터 이름에 매핑
    • 환경 레코드에 작성
  2. 함수 선언문을 function 오브젝트로 생성

  3. 함수 표현식과 변수에 초기값 설정

  4. 여기까지는 외부에 실행 상태를 제공하지 않음

<여기까지 모습>

실행 콘텍스트(EC): {
	렉시컬 환경 컴포넌트(LEC) = {
    	   환경 레코드(ER): {
              bonus: 70,
              point: undefined
           },
           외부 렉시컬 환경 참조(OLEC): {
              base: 200
           }
	},
    	변수 환경 컴포넌트(VEC): {},
    	this 바인딩 컴포넌트(TBC): {}
}

실행 단계

  1. 함수 안의 코드를 실행

  2. 실행 콘텍스트 안에서 관련된 함수와 변수를 사용할 수 있음


호출 스택(call stack)

  • call stack

    • 실행 콘텍스트의 논리적 구조
  • First in Last Out 순서

    • 함수가 호출되면 스택의 가장 위에 실행 콘텍스트가 위치하게 됨
    • 다시 함수 안에서 함수를 호출하면 호출된 함수의 실행 콘텍스트가 스택의 가장 위에 놓이게 됨
    • 함수가 종료되면 스택에서 빠져 나옴(FILO 순서)
  • 가장 아래는 글로벌 오브젝트의 함수가 위치

function one(){
  two();
  log(1);
};

function two(){
  three();
  log(2);
};

function three(){
  log(3);
};

one();

// 3
// 2
// 1
  • 함수가 호출되면 3개의 파라미터 값을 실행 콘텍스트로 넘겨 줌

    • 함수를 호출한 오브젝트
    • 함수 코드
    • 호출한 함수의 파라미터 값
  • 함수를 호출한 오브젝트를

    • this 바인딩 컴포넌트에 설정하여 this로 참조
  • 함수 코드

    • function 오브젝트의 [[Code]]에 설정되어 있음
  • 호출한 함수의 파라미터 값

    • 호출된 함수의 Argument 오브젝트에 설정

파라미터 값 매핑

  • 파라미터 값 매핑이란?
    • 호출한 함수에서 넘겨 준 파라미터 값을
    • 호출된 함수의 파라미터 작성 순서에 맞추어 값을 매핑하는 것
  • 엔진 처리 관점
    • 실행 콘텍스트로 넘겨 준 파라미터 값과
    • function 오브젝트의 [[FormalParameters]]에 작성된 이름에 값을 매핑하고
    • 결과를 선언적 환경 레코드에 설정하는 것

파라미터 이름에 값 매핑 방법

var obj = {};
obj.getTotal = function(one, two){
  return one + two;
}
log(obj.getTotal(11, 22, 77));

// 33
  1. 실행 콘텍스트로 넘겨준 파라미터 값을 param이라고 하자

  2. getTotal 오브젝트의 [[FormalParameters]]에서

    • 호출된 함수의 파라미터 이름을 구함 ➡ name이라고 가정
    • name은 ["one", "two"] 형태
    • [[FormalParameters]]는 function 오브젝트를 생성할 때 설정
  3. name 배열을 하나씩 읽음

  4. param에서 index번째의 값을 구함

    • 인덱스에 값이 없으면 undefined 반환
  5. name의 파라미터 이름과 4번에서 구한 값을

    • 선언적 환경 레코드에 {one: 11, two: 22} 형태로 설정
    • 같은 이름이 있으면 값이 대체됨
  6. name을 전부 읽을 때까지 3번에서 5번까지 반복


파라미터 값 할당 기준

var obj = {};
obj.getTotal = function(one, two){
  var one;
  log(one + two);
  two = 77;
  log("two:" + two);
}
obj.getTotal(11, 22);

// 33
// two:77

아래의 설명을 따라가면서 {key: value} 형태로 만들기

초기화 단계

  1. obj.getTotal(11, 22) 함수가 호출되면

    • 파라미터 값을 실행 콘텍스트로 넘겨줌
  2. 파라미터 이름에 값을 매핑하여

    • 선언적 환경 레코드에 설정
    • {one: 11, two: 22}
  3. var one;

    • 선언적 환경 레코드에서 one의 존재를 체크
    • 파라미터 이름을 설정하였으므로 존재하며 one을 기록하지 않음
  4. two = 77;

    • 선언적 환경 레코드에서 two의 존재를 체크
    • 파라미터 이름을 설정하였으므로 존재하며 two를 기록하지 않음
  5. 함수에 초기화할 코드가 없음

    • 첫 번째 줄로 이동하여 함수 코드를 실행

실행 단계

  1. 선언적 환경 레코드는 {one: 11, two: 22} 상태

  2. var one;

    • 단지 변수 선언이므로 처리하지 않음
  3. console.log(one + two);

    • 선언적 환경 레코드에서 one과 two 값을 구함
    • 11 + 22의 결과인 33이 [실행 결과]에 출력
  4. two = 77;

    • 값을 할당하는 코드이며 실행 단계이므로
    • 선언적 환경 레코드의 two에 77을 할당하며
    • {two: 22}가 {two: 77}로 변경됨
  5. console.log("two:" + two);

    • 선언적 환경 레코드에서 two 값을 구함
    • [실행 결과]에 two:77이 출력됨

정리 시간

var obj = {};
obj.getTotal = function(one, two, two){
  log(one + two);
}
obj.getTotal(11, 22, 55);

// 66
  • 위의 코드를 기준으로 엔진 관점에서 [실행 결과]에 출력된 값의 논리를 제시하세요.

    • 함수의 파라미터 값이 매핑되는 과정을 {key: value} 형태로 기술하고
    • 값이 출력되는 논리를 기술하세요.
  • 힌트

    • 두 번째, 세 번째 파라미터 이름이 같음

0개의 댓글