자바스크립트 - 실행 컨텍스트

pa324·2019년 11월 21일
0

실행 컨텍스트 (execution context)

  • 실행할 코드에 제공할 환경 정보들을 모아놓은 객체
    • 호이스팅,외부환경정보,this값 설정 ...
  • 동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고, 이를 콜 스택에 쌓아 올렸다가, 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 환경과 순서를 보장
  • 실행 컨텍스트를 구성하는 방법은 eval,전역공간,함수 실행이 있다.
var a = 1;
function outer() {
	function inner() {
    	console.log(a);
      	var a = 3;
    }
  inner();
  console.log(a);
}

outer();
consol.log(a)

스택 구조를 잘 생각해보면 한 실행 컨텍스트가 콜 스택의 맨 위에 쌓이는 순간이 곧 현재 실행할 코드에 관여하게 되는 시점임을 알 수 있다. 이렇게 어떤 실행 컨텍스트가 활성화될 때 자바스크립트 엔진은 해당 컨텍스트에 관련된 코드들을 실행하는 데 필요한 환경 정보들을 수집해서 실행 컨텍스트 객체에 저장한다.

실행 컨텍스트 구성

실행 컨텍스트의 수집 정보는 Lexical Environment, VariableEnvironment, ThisBinding이 있다.

VariableEnvironment

Variable Environment에 담기는 내용은 Lexical Environment와 같지만 최초 실행 시의 스냅샷을 유지한다는 점이 다르다. 실행 컨텍스트를 생성할 때 VariableEnvironment에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LexicalEnvironment를 만들고, 이 후에는 LexicalEnvironment를 주로 활용한다. 내부에는 environmentRecord와 outer-EnvironmentReference로 구성된다

LexicalEnvironment

Lexical은 흔히 정적 환경이라고 불린다. "재 컨텍스트의 내부에는 a,b,c와 같은 식별자들이 있고 그 외부 정보는 D를 참조하다록 구성되 있다 "라는 컨텍스를 구성하는 환경 정보들을 모아놓은 것 이라고 정의할 수 있다

  • environmentRecord와 호이스팅

    • 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다.
      • 별자는 성하는 함수에 지정된 매개변수 식별자, 선언한 함수가 있을 경우 그 함수 자체, var로 선언된 변수의 식별자가 있다
    • 컨텍스트 내부 전체를 처음부터 끝까지 탐색하면서 순서대로 수집한다.
    • 수집이 모두 완료되더라도, 실행 컨텍스트가 관여할 코드들은 실행되기 전의 상태이다.
      • 실행 전임에도 불구하고, 자바스크립트 엔진은 이미 해당 환경에 속한 변수명들을 모두 알고 있음
      • 자바스크립트 엔진은 식별자들을 최상단으로 끌어올려놓은 다음 실제 코드를 실행한다라는 성질 (호이스팅)
        • 변수는 선언부만 호이스팅 하고, 함수는 함수 전체를 호이스팅 시킨다.

function a() {

	console.log(b); // ... (1)
  	var b = 'bbb';
  	console.log(b); // ...(2)
 	function b() {}
  	console.log(b); // ...(3)
}
a();

  a를 실행하면서 실행컨텍스트가 생성된다. 그리고 environmentRecord를 채우기 위해서, 시작부터 끝까지 차례대로 탐색을 하면서 변수를 수집한다.
위의 예제에서 수집 대상은 var b function b() {}가 있다. 따라서 (1)에서는 'function b()'가 출력된다. var b는 선언부만 호이스팅 되었으므로, 메모리상에 b라는 이름을 가지고 있지만 데이터 영역은 아무것도 참조하지 않고 있다. 그리고 function b() {}가 호이스팅되면 function을 새로운 메모리 공간을 확보하고 해당 주소를 기존의 b주소의 데이터 영역에 맵핑 시킨다.
b = 'bbb' 를 실행하면, 'bbb'라는 문자열을 새로운 매모리 공간에 저장 시키고, b의 주소값에 문자열이 담긴 주소를 맵핑 시킨다. 따라서, (2),(3)에서는 'bbb'가 출력 된다.

함수 선언문 vs 함수 표현식

  • 함수 선언문은 정의부만 존재하고, 별도의 할당명령이 없는 것을 의미
    • 반드시 함수명을 정의해야 한다.
      • 기명 함수 표현식
  • 함수 표현식은 정의한 function을 별도의 변수에 할당하는 것을 말한다.
    • 함수명이 없어도 된다.
      • 익명 함수 표현식

function a() {} // 함수 선언문
a();

var b = function() {} // 함수 표현식 (익명 함수 표현식)
b();

var c = function d() {} // 함수 선언문 (기명 함수 표현식) 
c()
d() // 에러

console.log(sum(1,2)); //3
console.log(multiply(3,4)); // multiply is not a function error

function sum(a,b) {
	return a + b;
}

var multiply = function (a,b) {
	return a + b;
}

함수 선언문의 위험성

console.log(sum(3,4)) // ---(1)

function sum (x,y) {
	return x+y;
}

var a = sum(1,2); // ---(2)

//5000번째 줄에서 새롭게 추가됨
function sum(x,y) {
	return x + '+' + y '=' + (x+y)
}

var c = sum(1,2)
console.log(c) // ---(3)

최초에는 sum이라는 함수가 1+2의 합을 반환하도록 (1),(2) 모두 정상적으로 a+b의 합이 출력이 된다. 다른 개발자가 5000줄에 실수로 sum이라는 함수를 또 생성하고, return값을 문자열로 반환시키도록 했다. 함수 선언문은 함수 전체가 호이스팅 된다는 성질 때문에, 최초에 sum (a+b return함수)가 sum(문자열 return함수)으로 변경되게 된다. 따라서 (1)(2)가 의도와는 다르게 문자열을 출력하게된다. 이상황에서 5000번째 줄을 확인하기 전까지 원인을 찾을 수 없다. 만약 '함수 선언문'이 아닌 '함수 표현식'을 사용했다면 위와 같은 문제가 발생하지 않는다.

스코프,스코프 체인,outerEnvironmentReference

  • 스코프란 식별자에 대한 유효범위 정도로 정의할 수 있다(ES5이전에는 오직 함수에 의해서서만 생성이 되었다.)
  • 스코프 체인은 식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해 나가는 것을 의미한다.
    • LexicalEnvironment의 두번째 수집 자료인 outerEnvironmentReferencedlek로 인해서 스코프 체인이 가능
  • outerEnviron,entReference는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조한다.
    • 선언될 당시라는 의미는 어떤 실행 컨텍스트가 활성화된 상태를 의미
var a = 1;
var outer = function () {
	var inner = function() {
    	console.log(a); //(1) undefined
      	var a = 3; //a는 호이스팅되지만 변수가 호이스팅 되므로 값은 undefined
    }
    inner();
    console.log(a);//(2) 1
}
outer();
console.log(a);//(3) 1

위의 예제에서 (1)은 undefined가 출력된다. inner가 실행된느 시점에서 먼저 자신의 스코프 체인을 확인하는데, 이때 a가 호이스팅 되어서 자신의 스코프에 존재하는 것을 확인하고 상위 스코프를 탐색하지 않는다.

profile
안녕하세요

0개의 댓글