TIL 04 코어자바스크립트 #02 실행 컨텍스트

6mn12j·2021년 1월 19일
1

JS 쩨끼럽

목록 보기
4/7
post-thumbnail

📚코어 자바스크립트를 읽고 자바스크립트 동작 원리 이해하기, 자바스크립트 마스터의 길로...

📃 #02 실행 컨텍스트

실행 컨텍스트: 실행할 코드에 제공할 환경 정보들을 모아놓은 객체

01 실행 컨텍스트란?

실행 컨텍스트는 실행할 코드를 제공할 환경 정보들을 모아놓은 객체이다. 코드 실행시 필요한 환경 정보들을 모아 컨텍스트를 구성하고 , 이를 콜 스택(call stack)에 쌓아 올렸다가, 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드를 실행하는 식으로 전체 코드의 환경과 순서를 보장한다.

하나의 실행 컨텍스트(동일한 환경)
전역공간, eval()함수, 함수

실행 컨텍스트와 콜 스택
var a = 1;

function outer () {
    function inner (){
        console.log(a);
        var a = 3;
    }
    inner ();
    console.log(a);
}

outer();
console.log(a);

콜 스택에 쌓이는 순서로는
전역 컨텍스트 > outer > inner 순으로 아래에서 부터 쌓이고 inner함수가 종료되면 inner > outer > 전역 컨텍스트 순으로 제거 된다.

📌 콜스택 관련 개념은 갓니꼬 영상인 자바스크립트 개발자라면 알아야하는 핵심컨셉 33개 #01Call Stack 을 며칠전에 봤는데 해당 개념 이해할 때 도움이 될것 같아서 링크를 남긴다.

실행 컨텍스트가 콜 스택의 맨 위에 쌓이는 순간이 곧 현재 실행할 코드에 관여하게 되는 시점

자바스크립트 엔진은 해당 컨텍스트에 관련된 코드들을 실행하는데 필요한 정보들을 수집해서 실행 컨텍스트 객체에 저장하여 사용한다.

실행 컨텍스트 객체의 수집 정보

  • VariableEnvironment
    environmentRecord(snapshot) outerEnvironmentReference (sanpshot)
  • LexicalEnvironment
    environmentRecord outerEnvironmentReference
  • ThisBinding

VariableEnvironment

현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보를 담는다. 선언 시점의 의 스냅샷으로, 담기는 내용은 LexicalEnvironment와 같지만 최초 실행 시의 스냅샷을 유지한다는 점이 다르다. 내부의 environmentRecord, outerEnvironmentReference는 초기화 과정 중에는 동일하지만, 코드 진행시 달라진다. 변경 사항은 반영되지 않는다.

LexicalEnvironment

처음에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 반영된다.

environmentRecord와 호이스팅

environmentRecord 에는 현재 컨텍스트와 관련된 코드의 식별자 정보(매개변수의 이름, 함수선언, 변수명) 들이 저장된다. 이를 통해서 자바스크립트 엔진은 이미 해당 환경 코드 속의 변수명들을 모두 알고 있게 된다. 여기서 호이스팅 이라는 개념이 등장한다.

호이스팅

"자바스크립트 엔진은 식별자들을 최상단으로 끌어올려놓은 다음 실제 코드를 실행한다."

호이스팅 규칙

function a (x) {	//수집대상1 (매개변수)
 console.log(x); 	//(1)
 var x;			//수집대상2 (변수 선언)
 conosle.log(x);	//(2)
 var x = 2;		//수집대상3 (변수 선언)
 console.log(x);	//(3)
}

a(1);

호이스팅이 되지않을때의 예상 값

  • (1):1
  • (2):undefined
  • (3):2

실제 출력 값

  • (1):1
  • (2):1
  • (3):2

왜 예상값과 실제 출력 값이 다른지 호이스팅, 변수정보를 수집 하는 과정을 보자.

호이스팅 과정

1. 매개변수와 변수에 대한 호이스팅

environmentRecord는 수집시 어떤 식별자 들이 있는지에만 괌심이 있고, 각 식별자에 어떤 값이 할당될 것인지는 관심이 없습니다. 즉, 관심 있는 변수명(식별자)만 끌어올리고 할당과정은 원래 자리에 그대로 남겨 둡니다.

호이스팅 적용시 아래와 같은 코드가 된다.

function a (x) {	//수집 대상1 (매개변수)
 var x;			//수집 대상 1의 변수 선언
 var x;			//수집 대상 2의 변수 선언
 var x;			//수집 대상 3의 변수 선언
 
 x = 1; 		//수집 대상 1의 남겨진 할당 부분 
 console.log(x);	//(1)
 conosle.log(x);	//(2)
 x = 2;			//수집 대상3의 할당 남겨진 할당 부분
 console.log(x);	//(3)
}

a(1);
2. 함수 선언의 호이스팅

매개변수와 변수는 선언부와 할당부를 나누어 선언부만 끌어올리는 반면 함수 선언은 함수 전체를 끌어 올립니다.

function a (){
  console.log(b);		//(1)
  var b = 'bbb';		//수집 대상 1(변수선언)
  console.log(b);		//(2)
  function b () { }		//수집 대상2(힘수전체)
  console.log(b);		//(3)
}
a();

호이스팅이 되지않을때의 예상 값

  • (1):undefined
  • (2):'bbb'
  • (3):b함수

실제 출력 값

  • (1):b함수
  • (2):'bbb'
  • (3):'bbb'

호이스팅 적용시 아래와 같은 코드가 된다.

function a (){
  var b;		//수집 대상 1의 변수 선언
  var b = function b () { };	//수집 대상 2의 함수전체
  //편의상 함수명 b에 함수를 할당한 것처럼 여긴다.
  
  console.log(b);	//(1)
  b = 'bbb';		//수집 대상 1의 남겨진 할당 부분
  console.log(b);	//(2)
  console.log(b);	//(3)
}
a();

함수를 정의하는 세 가지 방식

javascript 함수 정의할때는 세가지의 방식이 있다.
1.함수 선언문

//함수 선언문. 함수명 a 가 곧 변수명
function a() { /*... */ } 
a();

2.(익명) 함수 표현식

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

3.기명 함수 표현식

//기명 함수 표현식. 변수명은 c, 함수명은 d.
car c = fcuntion d() {/*... */}
c();
d(); //에러!

기명 함수 표현식은 주의할 점이있다.바로 외부에서는 함수명으로 함수를 호출할 수 없다는 점이다. 함수 내부에서 재귀함수를 호출하는 용도로 쓸 수있지만.
굳이 사용할 필요가 있나?

함수 선언문과 함수 표현식의 실질적인 차이를 알아보는 예제 이다. lexicalEnvironment가 수집하는 두가지 정보중 environmentRecord의 정보 수집 과정에서의 호이스팅을 살펴보는 중이다.

아래 코드를 호이스팅 할 시 , 함수 선언문은 함수 전체를 호이스팅 하는 반면 함수 표현식은 변수 선언부만 호이스팅이 된다. 그렇게 되면 console.log(multiply(3,4) 보다 앞에는 var multiply 변수 선언과정만 호이스팅 되므로 값(함수)가 할당돼 있지 않아 에러 메시지가 출력된다. 원할한 협업 과정을 위해서는 함수 표현식을 사용해야 한다.

상대적으로 함수 표현식이 더 안전하다 -> 함수 표현식을 사용하자 !

console.log(sum(1,2));
console.log(multiply(3,4));

function sum (a,b) { //함수 선언문 sum
	return a+b;
}

var multiply = function (a,b) {// 함수 표현식 mulriply
	return a * b;
}

전역 컨텍스트가 활성화 될때 전역공간에 선언된 함수들은 모두 호이스팅 되서 모두 가장 위로 끌어올려진다. 동일한 변수명에 서로 다른 값을 할당할 경우에는 나중에 할당한 값이 오버라이딩 된다. 따라서 함수 선언문 을 사용하면 에러를 알아내기가 어렵기 때문에 함수 표현식으로 정의하는게 안전하다.

스코프, 스코프 체인 outerEncironmentReference

스코프
스코프란 식별자에 대한 유효범위이다. A 내부에서 선언한 변수는 오직 A의 내부에서만 접근 가능한게 스코프의 개념이다. ES5까지의 자바스크립트는 특이하게도 전역 공간을 제외하면 오직 함수에 의해서만 스코프가 생성된다 이를 함수 스코프라 했다. ES6에서는 let, const, class, strict mode를 사용하면서 다른 프로그래밍 언어와 훨씬 비슷하게 블록 크소프를 사용 할 수 있다.

스코프 체인
'식별자의 유효범위'를 안에서부터 바깥으로 차례로 검색해나가는 것을 스코프 체인 이라고 한다. 이를 가능하게 하는 것이 LexicalEnvironment를 의 두번째 수집 자료인outerEnvironmentReference이다.

outerEnvironmentReference는 현재 호출된 함수가 선언될 당시LexicalEnvironment를 참조한다. 콜 스택의 구조상 여러 스코프에서 동일한 식별자를 선언한경우에는 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능 하게 된다.

전역변수와 지역변수
전역 공간에서 선언한 변수는 전역변수
함수 내부에서 선언한 변수는 지역변수

📃 정리

실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
실행 컨텍스트 객체는 활성화되는 시점에 VariableEnvironment(V.E), LexicalEnvironment(L.E), ThisBindeing의 세가지 정보를 수집한다.

실행 컨텍스트 생성할 때는 V.E와 L.E가 동일한 내용으로 구성되지만 L.E는 함수 실행 도중에 변경되는 사항이 즉시 반영된다.

L.E는 매개변수명, 변수의 식별자, 선언한 함수으 함수명 등을 수집하는 environmentRecord(E.R)와 바로 직전 컨텍스트의 L.E 정보를 참조하는 outerEnvironmentReference로 구성돼 있다.

호이스팅은 실행 컨텍스트가 관여하는 코드 를 최상단으로 끌어올린다. 변수 선언과 값 할당 시에는 '선언부'만 끌어올리고, 할당 과정은 원래 자리에 남아있게 되는데, 여기서 함수 선언문과 함수 표현식의 차이가 발생한다. 함수 표현식이 더 안전하다.

전역 컨텍스트의 L.E에 담긴 변수를 전역변수라 하고, 그 밖의 함수에 의해 생성된 실행 컨텍스트의 변수들은 모두 지역 변수이다. 안전한 코드 구성을 위해서는 전역변수의 사용은 최소화.

this는 다음장에서 나오는데 실행 컨텍스트 활성화당시에 지정된 this가 저장된다. 함수 호출시 값이 지정되지 않는 경우에는 전역 객체가 저장된다.

한번 읽고 정리하면서 한번 더 읽었는데도 .... 넘 어렵다 ...

호이스팅 개념은 '끌어올린다' 만 알고 있었는데 예제를 보면서 설명하니까 잘못 알고 있던 부분도 있었다.

profile
TIL 쩨끼럽 남기는 중 💻

0개의 댓글