티스토리 뷰

프로그램에서 함수를 실행시키면 Call Stack에 함수 호출 정보가 쌓이게 되고, 변수나 인자같은 함수 실행에 필요한 정보를 유지하고 실행 과정을 추적하는데 사용된다.

마찬가지로 자바스크립트도 함수 호출 시 이런 정보를 관리하는데, 이를 실행 컨텍스트라고 부른다. 함수가 호출되면 실행 컨텍스트가 생성되고, 실행 컨텍스트 스택에 쌓인다. 스택 가장 위에 위치하는 실행 컨텍스트가 현재 실행되고 있는 컨텍스트다.

[그림 1] 실행 컨텍스트 스택

실행 컨텍스트 생성 과정

아래 코드와 같은 함수가 있을 때, 함수를 호출해서 실행 컨텍스트가 생성된 뒤 실행 컨텍스트가 어떻게 쓰이는지 알아보자.

function func(arg1, arg2) {
    let a = 1, b = 2;
    function func2() {
        return a + b;
    }
    
    return arg1 + arg2 + func2();
}

func(3, 4); // 10

 

실행 컨텍스트가 생성되면 함수 실행에 필요한 정보들을 담기 위한 활성 객체를 생성한다. 활성 객체는 지역 변수, 매개 변수, 함수 선언을 가지고 있는 객체다.

[그림 2] 활성 객체

활성 객체가 생성된 후에는 arguments 객체를 생성한다. 위에서 만든 활성 객체의 arguments 프로퍼티로 생성된 arguments 객체를 참조한다.

[그림 3] arguments 객체 생성

실행 컨텍스트의 유효 범위를 나타내는 스코프 정보를 생성한다. 연결 리스트 형태로 이런 스코프 정보를 관리하는데, 이를 스코프 체인이라고 부른다. 함수 내에서 변수를 참조하면 스코프 체인을 따라서 찾게 된다. (프로토타입 체인과 비슷한 방식이다.) 스코프 체인은 [[scope]]라는 프로퍼티로 저장된다.

[그림 4] 스코프 체인 생성

매개 변수가 초기화 되고 지역 변수, 함수를 생성한다. 초기화와 생성을 나눠서 말하였는데, 지역 변수와 함수는 실제로 사용하기 전까지 메모리에 할당만 되고 실제로 초기화되지 않기 때문이다. 따라서 아래 그림 5처럼 지역 변수 a, b는 undefined인 상태로 생성만 돼있는 상태가 된다.

[그림 5] 매개 변수, 지역 변수 초기화 및 생성

마지막으로 this가 바인딩된다.

[그림 6] this 바인딩

전역 실행 컨텍스트

전역 실행 컨텍스트는 전역 코드가 실행될 때 생성된다. 전역 실행 컨텍스트는 arguments 객체가 없고 전역 객체 하나만을 포함하는 스코프 체인이 있다는 점이 일반 실행 컨텍스트와 다르다.

전역 실행 컨텍스트는 가장 상위 컨텍스트이므로 전역 실행 컨텍스트의 활성 객체가 전역 객체가 된다. 따라서 전역으로 선언한 변수와 함수는 전역 객체의 프로퍼티가 된다.

스코프 체인

다른 언어와 마찬가지로 자바스크립트도 변수와 함수의 유효범위(스코프)가 있다. C언어의 경우 스코프가 중괄호가 단위가 되지만 자바스크립트는 오직 함수만이 스코프의 단위가 된다.

위에서 설명했듯이 실행 컨텍스트의 활성 객체에는 [[scope]]라는 이름의 프로퍼티가 있고, 이 프로퍼티가 함수의 유효 범위를 나타낸다. [[scope]] 프로퍼티는 연결리스트 형식으로 유효 범위를 관리하는데, 이를 스코프 체인이라고 부른다.

 

아래 코드를 실행시켜보자.

function func() {
    let a = 1, b = 2;
    function func2() {
        let c = 3, d = 4;
        function func3() {
            return a + b + c + d;
        }

        console.dir(func3);
        return func3();
    }

    console.dir(func2);
    return func2();
}

console.log(func()); // 10

위 코드는 func 내부에서 func2 함수를 만들고 또 그 안에서 func3 함수를 만들고 있다. 그리고 func2와 func3의 구조를 출력하는 모습을 볼 수 있는데, 아래 그림 7과 같다.

[그림 7] 스코프 체인

위 그림 7을 통해 함수 객체가 생성될 때, 생성될 당시의 컨텍스트를 [[scope]] 프로퍼티에 가지고 있다는 것을 알 수 있다. 함수를 실행하면 위에서 설명한 실행 컨텍스트 생성의 과정을 거친 후, 생성될 당시의 컨텍스트에 새로 생성된 실행 컨텍스트를 더하여 스코프 체인을 가지고 있는 것이다.

함수 호이스팅의 원인

이전 글에서 함수에 대해 공부할 때, 함수 선언문과 함수 표현식의 차이가 호이스팅이 일어나는지 안일어나는지 차이라고 했다. 위에서 설명한 실행 컨텍스트의 생성 과정을 이해했다면 호이스팅의 원인을 알 수 있다.

func1();

// 함수 선언문
function func1() {
    console.log('func1');
}
func2();

// 함수 표현식
let func2 = function() {
    console.log('func2');
}

위 코드에서 함수 선언문으로 정의한 함수는 잘 실행되는 반면, 함수 표현식으로 정의한 함수는 아래 그림 8과 같은 에러가 나타난다.

[그림 8] 함수 호출 에러

함수 표현식으로 선언했을 때 에러가 나는 이유는 전역 객체에 func2 변수가 생성됐지만, 할당될 함수의 초기화가 아직 안 된 상태에서 호출했기 때문이다. 이해가 잘 안된다면 위에서 설명한 실행 컨텍스트 생성 과정을 다시 한 번 보기 바란다.

반면에 함수 선언문으로 정의한 함수는, 함수 객체가 이미 메모리에 올라와 있는 상태에서 호출했기 때문에 정상적으로 호출된 것이다.

'프로그래밍 > Javascript' 카테고리의 다른 글

자바스크립트 클로저  (0) 2019.11.12
자바스크립트의 실행 컨텍스트  (0) 2019.11.08
프로토타입 체이닝  (0) 2019.11.07
자바스크립트 this  (0) 2019.11.07
자바스크립트 함수  (0) 2019.11.06
자바스크립트 데이터 타입  (0) 2019.11.06
댓글
댓글쓰기 폼