본문으로 바로가기

2장 실행 컨텍스트

category 코어 자바스크립트 2020. 12. 21. 15:37

실행 컨텍스트는 자바스크립트의 중요한 개념중 하나이다

클로저, 호이스팅, this에 대한 이해를 할려면 기본적으로 실행 컨텍스트가 어떤 개념인지 먼저 이해해야 한다

 

실행 컨텍스트란 실행할 코드에 제공할 환경 정보들을 모아놓은 객체로, 자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념이다

 

실행 컨텍스트를 파악하려면 제일 먼저 스택 의 개념을 알아야 한다

 

스택과 큐

스택은 Last in, first out 구조이고 <예) 장독대 안에 쌓아서 넣어 둔 김치 꺼내기>

큐는 First in, first out 구조이다 <예) 놀이공원 입장하기>

 

실행 컨텍스트는 동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이를 콜 스택 에 쌓아 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장한다

 

 

이런 코드가 있다고 하자

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

 

위 코드의 실행 컨텍스트는 이렇다

코드를 실행하는 순간 먼저 콜스택에 전역 컨텍스트가 담기고 함수가 하나씩 순서대로 실행되게 된다

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

맨 위에 쌓인 outer 함수 내부의 코드를 순차로 실행하다가 inner를 읽고 나면

inner 함수의 실행 컨텍스트가 그 위에 담기게 되고 

실행이 끝나게 되면 하나씩 콜 스택에서 제거된다

 

이런 이유로 제일 위에 있는 함수는 아래의 함수를 참조 할 수 있지만 

아래의 함수는 위의 함수내부를 참조 할 수 없다

 

 

위의 코드에서 inner()의 내부에 있는 a가 3이 아니라 undefined라는게 또 의문이 드는데 

여기에서 호이스팅의 개념이 등장한다

 

 

호이스팅

앞서 실행 컨텍스트를 실행할 코드에 제공할 환경 정보들을 모아놓은 객체 라고 했다

이 객체에 담기는 정보 다음과 같다

1. VariableEnvironment

2. LexicalEnvironment

3. ThisBinding

 

이것에 대한 정보는 meetup.toast.com/posts/129 여기나 다른곳을 참조하도록 하자.. 어렵다

 

1. VariableEnvironment 와 2. LexicalEnvironment 의 내부는 environmentRecord와 outer-EnvionmentReference로 구성되어 있다

 

environmentRecord는 식별자들의 바인딩을 기록하는 객체를 말한다. 간단히 말해 변수, 함수 등이 기록되는 곳이다.

 

environmentRecord가 변수 정보를 수집하는 과정을 모두 마쳤더라도 아직 실행 컨텍스트가 관여할 코드들은 실행되기 전의 상태이다

코드가 실행되기 전임에도 불구하고 자바스크립트 엔진은 이미 해당환경에 속한 코드의 변수명들을 모두 알고 있게 되는 셈이다.. 

그래서 쉽게 생각하면 자바스크립트 엔진은 식별자들을 최상단으로 끌어올려 놓은 다음 실제 코드를 실행한다 라고 보면 된다

 

그래서 위의 코드를 다시 가져오면

var a = 1;
function outer() {
    function inner() {
        console.log(a); // undefined << 이부분
        var a = 3;
    }
    inner();
}
outer();

inner 함수 내부에 있는 console.log(a) 가 실행될 때 이미 inner는 호이스팅되어 var a; (a 를 선언하는 것) 을 먼저 읽었기 때문에 a는 undefiend가 된다

 

호이스팅 된 코드의 동작은

var a = 1;
function outer() {
    function inner() {
        var a;
        console.log(a); // undefined << 이부분
        a = 3;
    }
    inner();
}
outer();

이런 식으로 이루어지게 된다.

 

호이스팅이 이런규칙 때문에 문제가 생기기도 한다

그래서 var 말고 ES6에는 let과 const가 생겨났다.  

let과 const는 다음에 살펴보기로 하자

 

 

함수의 호이스팅

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

여기서 함수의 호이스팅과 변수의 호이스팅은 차이를 보여준다

변수는 선언부와 할당부를 나누어 선언부만 끌어올리는 반면,

함수 선언은 함수 전체를 끌어올린다

 

그래서 호이스팅 된 코드의 동작문은

function a () {
    var b;
    function b () {}
    
    console.log(b);
    b = 'bbb';
    console.log(b);
    console.log(b);
}

이런식으로 된다

 

 

함수 선언문과 함수 표현식

foo(); //잘 작동됨
function foo(){  //함수 선언문
    console.log('foo');
}

 

foo(); // error
var foo = function () { //함수 표현식
    console.log('foo');
}

함수 선언문과 표현식은 차이가 있다

함수 선언문은 호이스팅 되어 호출을 먼저하고 나중에 선언 해도 동작 하지만

함수 표현식은 변수 선언부만 호이스팅을 하게 된다

 

그래서 이 책에서는 같은 이름을 사용했을 때 문제가 생길 위험이 크고 디버깅이 어려운 함수 선언문 보다 함수 표현식을 사용하는 것을 권하고 있다

 

 

익명함수 표현식과 기명함수 표현식

보통 우리가 사용하는 표현식은 익명함수 표현식이다

기명 함수표현식은 이런식이다

var c = function d () {}  //기명 함수 표현식

기명 함수 표현식을 사용한다고 해서 외부에서 d()로 호출할 수는 없다

대신에 함수 내부에서 d()를 호출할 수는 있다 //ex) 재귀함수

사실 함수 내부에서 다시 c()를 호출해도 같은 동작을 한다

 

위와같은 이유로 이 책에서는 기명함수 표현식을 사용해야 할 필요가 있을지는 의문이라고 한다 

하지만 ko.javascript.info/function-object#ref-650 여기를 참조한 결과 필요한 상황이 있기는 한 것 같다

let sayHi = function(who) {
  if (who) {
    alert(`Hello, ${who}`);
  } else {
    sayHi("Guest"); // TypeError: sayHi is not a function
  }
};

let welcome = sayHi;
sayHi = null;

welcome(); // 중첩 sayHi 호출은 더 이상 불가능합니다!

여기에서 sayHi가 자기자신을 호출하게 되는데 

sayHi를 null로 변경하면 welcome은 해당 기능을 사용할 수 없다.

 

그럴땐 아래와 같은 기명함수 표현식을 사용해야 한다

let sayHi = function func(who) {
  if (who) {
    alert(`Hello, ${who}`);
  } else {
    func("Guest"); // 원하는 값이 제대로 출력됩니다.
  }
};

let welcome = sayHi;
sayHi = null;

welcome(); // Hello, Guest (중첩 호출이 제대로 동작함)

 

 

스코프, 스코프 체인

스코프 식별자에 대한 유효범위이다

어떤 경계 A의 외부에서 선언한 변수는 A의 외부뿐 아니라 A의 내부에서도 접근이 가능하지만, A의 내부에서 선언한 변수는 오직 A의 내부에서만 접근할 수 있다

이러한 식별자의 유효범위를 안에서부터 바깥으로 차례대로 검색해 나가는 것을 스코프 체인이라 한다

 

위에서 실행 컨텍스트를 설명할때 

콜 스택의 제일 위에 있는 함수는 아래의 함수(아래의 컨텍스트)를 참조 할 수 있지만

아래의 함수는 위의 함수내부를 참조 할 수 없다고 했다

 

간단하게 말해서 스코프 체인은 아래에 있는 실행컨텍스트를 참조 할 수 있다고 보면된다

 

스코프 체인은 변수 은닉화, 클로져를 사용할 때 더 확실하게 확인이 가능하다

 

'코어 자바스크립트' 카테고리의 다른 글

7장 클래스  (0) 2020.12.28
6장 프로토타입  (2) 2020.12.24
5장 클로저  (0) 2020.12.23
4장 콜백함수  (0) 2020.12.22
3장 this  (2) 2020.12.21
1장 메모리와 데이터 타입  (0) 2020.12.21