CoreJavaScript_02.실행컨텍스트(2)

손병진·2020년 11월 3일
0

CoreJavaScript

목록 보기
4/10

해당 내용은 '코어 자바스크립트' 서적을 참고하여 작성되었으며, 초보 개발자에게 유익한 책을 지필해주신 지은이 정재남 님께 감사의 말씀을 드립니다.

실행컨텍스트

호이스팅(environmentRecord)

  • 호이스팅이란?
    '끌어올리다' 라는 의미로 hoist 동사에 ing 진행형을 덧붙인 단어이다.
  • 실행컨택스트가 구성되고 environmentRecord 정보가 수집될때 일어나는 현상으로
    식별자(변수명)의 정보에 대해서만 우선적으로 수집하여 인식하고(이를 변수를 끌어올린다고 표현한다)
    그 다음에 차례대로 할당하는 식으로 진행된다.
function hoist(x) {
  console.log(x); // 1

  var x;
  console.log(x); // 1

  var x = 2;
  console.log(x); // 2
}

hoist(1);// 실행컨택스트 구성 시작!
// 위 코드에 호이스팅 현상을 적용하여 표현하자면 아래와 같이 다시 나타낼 수 있다.

function hoist() {
  // 식별자 정보(environmentRecord)만을 끌어올린다
  var x;
  var x;
  var x;

  // 그 다음 할당이 이루어진다
  x = 1;
  console.log(x);

  console.log(x);

  x = 2;
  console.log(x);
}

hoist();

함수 선언문과 함수 표현식

  • 두 개념의 차이는 일단 구조가 다르다
// 함수 선언문
function func(){}

// 함수 표현식
var func = function(){}
  • 그리고 호이스팅 현상에서도 다른 방식으로 인식된다.
declare(); // a

function declare() {
  console.log('a');
}

express(); // error: express is not a function

var express = function () {
  console.log('a');
};
/* 왜 그럴까?
위 코드에 호이스팅 현상을 적용하여 다시 표현하자면 다음과 같다*/

// 함수 선언문은 호이스팅 적용시 함수 전체를 끌어올린다
function declare() {
  console.log('a');
}
// 함수 표현식은 호이스팅 적용시 식별자만 끌어올린다
var express;

declare();

express(); // 그래서 express 할당된 함수가 없기 때문에 에러가 발생한다

express = function () {
  console.log('a');
};

함수 선언문 vs 함수 표현식 : 무엇이 더 권장되는가
결론적으로, 함수 표현식을 권장하고 있다. 왜냐하면 개발자가 코드를 볼때 위에서 아래로 읽어가며, 함수를 만든 뒤에 그 함수를 실행되어야 혼란이 없기 때문이다. 호이스팅 현상 같은 경우 맨 마지막에 선언된 변수를 기준으로 할당 및 실행이 이루어진다. 그래서 만약 같은 이름이지만 다른 내용의 함수 선언문이 두개 존재한다면 나중에 선언된 함수가 전역으로 적용되는 불상사가 일어날 수 있다. 하지만 함수 표현식으로 한다면 차례대로 다른 내용이 할당될 수 있기 때문에 원하는 순서대로 함수가 적용된다. 그리고 함수 선언 이전에 실행됐을 때에 에러가 발생하여 디버깅에도 용이하다.


스코프체인(outerEnvironmentReference)

  • 스코프(scope)란?
    식별자에 대한 유효범위

  • 스코프체인이란?
    '식별자의 유효범위'를 안에서부터 바깥으로 차례로 검색해나가는 것

  • outerEnvironmentReference
    스코프체인을 가능하게 만드는 도구이자, LexicalEnvironment의 두번째 수집 자료
    (첫번째는 environmentRecord)

    • 그렇다면 outerEnvironmentReference 정보는 어디서 어떻게 수집할까?
      해당 함수가 선언되는 위치(실행컨택스트)의 LexicalEnvironment 정보를 가져온다.
      앞선 블로깅해서 언급했던 '외부 환경 정보'가 이에 해당한다.
      상위 실행컨택스트의 LexicalEnvironment 정보를 현재 실행컨택스트의 outerEnvironmentReference 에 저장한다.
      그래서 현재 실행컨택스트의 LexicalEnvironment 내에는 내부 식별자 정보인 environmentRecord 와 외부 환경 정보인 outerEnvironmentReference가 있는 것이다.
// LexicalEnvironment = LE / outerEnvironmentReference = OE

var a = 1;
var outer = function(){ // 2. 이 시점에서의 LE 정보가 outer 함수의 OE 정보로 들어간다
  var inner = function(){ // 4. 이 시점에서의 LE 정보가 inner 함수의 OE 정보로 들어간다
    console.log(a);
    var a = 3;
  }
  inner(); // 3. inner 실행컨택스트 구성 시작!
  console.log(a);
}
outer(); // 1. outer 실행컨택스트 구성 시작!
console.log(a);
  • 위 예시 추가 설명
    2번 시점에서 받는 OE 정보는 전역컨택스트의 LE 정보이며, [Global, {a, outer}] 이다.
    4번 시점에서 받는 OE 정보는 outer의 LE 정보이며, [outer, {inner}] 이다.

    • 변수 은닉화
      inner 함수 내부의 console.log(a) 에서 식별자 a 정보로 접근하려 할때 순서가 있다.
      먼저, 현재 실행컨택스트의 environmentRecord(이하 ER) 정보를 탐색한다.
      만약 없으면 OE 정보(상위 실행컨택스트의 LE)로 접근한 다음 그 안의 ER 정보를 탐색한다.
      그런데 위의 상황같은 경우,
      inner 함수 내부에서 변수 선언이 이루어졌기 때문에 OE 정보로 접근하지 않아 외부 환경에 있는 변수 a 정보를 가져올 수 없는데 이를 변수 은닉화 라고 한다.
  • 전역변수와 지역변수
    위에서 설명한 스코프와 변수 은닉화의 연장선으로 이해할 수 있는 개념이다.
    전역 공간에서 선언한 변수를 전역변수, 특정 함수 내에서 선언한 변수를 지역변수 라고 한다.
    코드에 혼란을 줄이기 위해서는 가급적 전역변수를 줄이는게 좋다고 한다.

profile
https://castie.tistory.com

0개의 댓글