티스토리 뷰

이전 글에 이어서 JavaScript 스코프에서 파생된 개념들에 대해 알아보려고한다.

호이스팅 (Hoisting)

Hoisting이라는 단어를 직역하면 끌어올리기, 들어 올려 나르기라는 뜻이라고 한다.
JavaScript의 호이스팅 도 비슷한 의미를 가지고 있다. 변수 호이스팅과 함수 호이스팅으로 나누어 자세히 설명해보도록 하겠다.

변수 호이스팅

변수를 선언하고 초기화했을때, 선언 부분이 최상단으로 끌어올려지는 현상 을 말한다. 즉, 변수가 함수 내에서 정의되었을 경우 함수의 최상단으로, 함수 바깥에서 정의되었을 경우는 전역 컨텍스트의 최상단으로 변경된다.

function hoisting() {
    console.log(foo); // undefined
    var foo = 'javascript';
    console.log(foo); // javascript
}
hoisting();

위와 같은 코드가 있을 때, 코드 순서 상 변수 foo가 선언되기 전에 출력하면 다른 언어의 경우엔 에러가 발생할 것이다.
하지만 JavaScript에서는 호이스팅이라는 특성 때문에 undefined만 출력하고 에러는 발생하지 않는다.
(undefined가 출력되는 이유는 undefined vs null 를 참고해주세요!)

작동 순서에 맞게 코드를 재구성하면 아래와 같다.

function hoisting() {
    var foo;
    console.log(foo); // undefined
    foo = 'javascript';
    console.log(foo); // javascript
}
hoisting();

즉, var foo = 'javascript' 에서 var foo를 호이스트하기 때문이다.

함수 호이스팅

함수 호이스팅도 같은 맥락으로, 함수 선언보다 호출을 먼저해도 선언식이 최상단으로 끌어올려지기 때문에 정상적으로 호출된다.

hoisting(); // javascript
function hoisting() {
    console.log('javascript'); 
}

하지만 같은 함수여도 함수를 표현식으로 선언한 경우 에는 호이스팅에 영향을 받지 않기 때문에 에러가 발생한다.

hoisting(); // Syntax Error
var hoisting = function () {
    console.log('javascript'); 
}

실행 문맥(Execution Context)

JavaScript는 실행 시점에 실행 문맥(Execution Context, EC) 이 생성되고, 그 시점에 우리 눈에 보이지않는 온갖 행위들이 이루어진다. 앞서 설명한 호이스팅도 여기서 발생하는 것이다.

즉, 실행 문맥은 코드의 실행 정보를 나타내며 함수를 호출하거나 eval(...)호출, 전역 코드 실행 시 새롭게 생성된다.

실행 문맥은 객체의 형태를 가지며 아래의 3가지로 이루어져 있다.

  • 활성 객체(Activation Object): 실행 문맥 내부에서 사용되는 arguments 객체와 변수를 저장하는 객체
  • 유효 범위(Scope) 정보: 현재 실행되고 있는 컨텍스트의 유효범위가 담긴 정보
  • this 객체: 현재 실행 문맥을 포함하는 객체

실행 문맥 생성 순서

function ecTest(pram1, pram2) {
  var localVar = pram1 + pram2;
  return localVar;
}
ecTest(1, 2, 3);

위 코드를 실행하게 되면 다음과 같은 순서로 실행 문맥이 생성된다.

  1. 활성 객체 생성
  2. arguments 객체 생성
  3. 유효 범위 정보 생성: 스코프 체인의 생성과 초기화
  4. 변수 생성
  5. this객체 바인딩
  6. 실행

여기서 arguments 객체 는 함수로 들어오는 인자를 모두 모아놓은 유사 배열 객체이다. (arguments: [1, 2, 3])
스코프 체인(Scope Chain) 은 유효범위 정보를 담는 일종의 리스트이다.
변수들은 지역변수와 매개변수 pram1, pram2, localVar 이며, 매개변수는 실행 문맥 생성 단계에서 초기화 값이 들어가지만, 지역변수 localVar 는 생성 후 실행 단계에서 호이스트 되기 때문에 undefined란 값을 가지고 생성된다.

스코프 체인(Scope Chain)

위에서 언급된 스코프 체인은 전역 객체와 중첩된 함수 스코프의 레퍼런스를 차례대로 저장하는 일종의 리스트 를 말한다.
앞서 실행 문맥 생성에서 함수가 실행될 때 스코프를 생성하고, 해당 함수를 호출한 부모 함수가 가진 활성객체가 리스트에 추가되어 상/하위 객체간에 부모/자식 관계 가 형성 되는 것이다.

아래 예제 코드를 통해 더 자세히 설명하자면,

function wrapper() {
  var a = 1;
  function inner() {
    console.log(a);
  }
  inner();
}
wrapper();

위의 코드에서 wrapper(), inner()가 실행되면서 생성된 실행문맥에 의해서 inner() 함수 내의 변수 a를 탐색하는데, 만약 inner()에 변수 a가 없다면 inner()를 감싸고 있는 부모함수 wrapper()를 탐색하게 된다. 이때 변수 a가 존재하면 참조하고, 없다면 계속적으로 감싸고 있는 상위 함수를 탐색 하는 과정을 거치게 된다.

아마 스코프 체인에 대해서는 자연스럽게 알고 있었지만, 스코프 체인이라는 명칭이나 정확히 어떤 방식으로 동작하는지는 모르는 사람들이 꽤 있을 것 같다.

정리

JavaScript는 함수를 호출하거나 전역 코드가 실행되면 실행 문맥이 생성되고, 렉시컬 스코프 규칙에 따라 내부 함수와 내부 함수에 연결된 외부 함수의 정보를 탐색할 수 있게된다. 이러한 JavaScript 스코프의 특성 때문에 이 글에서 설명한 호이스팅이 발생하며, 앞으로 설명할 클로저라는 중요한 기능도 사용할 수 있게 된 것이다.

Reference

'JavaScript' 카테고리의 다른 글

커링 (Currying)  (0) 2018.12.14
클로저 (Closure)  (0) 2018.12.09
스코프 (Scope)  (0) 2018.12.09
웹 컴포넌트 (Web Component)  (0) 2018.12.07
undefined vs null  (0) 2018.12.06
댓글