1. 실행 컨텍스트

 1) 실행 컨텍스트: 실행할 코드에 제공할 환경 정보들을 모아놓은 객체

  -선언된 변수, 함수 등을 실행 컨텍스트에 저장해두었다가 호출하면 나옴

 

 2) 스택(stack): 우물 같은 구조로 가장 처음 넣은 데이터는 데이터가 쌓일 수록 밑에 깔리게 되어 가장 마지막에 나오고

                              가장 마지막에 넣은 데이터가 가장 처음 나오는 구조

 3) 큐(queue): 양쪽이 모두 열린 파이프 구조로 한쪽으로 입력되어 다른 쪽으로 출력, 가장 먼저 들어간 데이터가 가장 먼저 나옴

 

 4) 예시

//------------------------------전역 컨텍스트 생성
var a = 1;

function outer() { //----------outer 컨텍스트 생성
  function inner() { //--------inner 컨텍스트 생성
    console.log(a); //---------(1) undefined
    var a = 3; // inner 함수가 끝남과 동시에 사라짐
  }
  inner();
  console.log(a); //----------(2) 1
}

outer();

console.log(a); //-----------(3) 1

  -저장

   (1) 전역 컨텍스트가 생성되어 스택 가장 아래에 저장되고, 위에서부터 차례대로 실행

   (2) outer 함수가 호출되며 전역 컨텍스트 위에 저장되고, outer 함수의 코드 실행

   (3) outer 함수 내의 inner 함수가 호출되며 ouer 함수 컨텍스트 위에 저장되고, inner 함수의 코드 실행

   (4) inner 함수의 마지막 코드가 실행되면 다시 outer 함수로 돌아와 나머지 코드 실행

   (5) outer 함수의 마지막 코드가 실행되면 다시 전역으로 돌아와 나머지 코드 실행

   (6) 전역 컨텍스트의 마지막 코드가 실행되면 전역 컨텍스트까지 제거되고 콜 스택에는 아무것도 남지 않음

 

 

  -컨텍스트가 콜 스택에 있을 때만 컨텍스트 내부의 환경변수들이 현재 실행하는 코드에 관여할 수 있음

  →inner 함수 내부에서 선언한 var a = 3은 inner 함수가 콜 스택에서 나감과 동시에 사라짐

  →전역에서 선언한 var a = 1은 전역 컨텍스트가 사라지는 마지막까지 남아서 console.log(a)를 실행할 때 적용됨

 

 

2. VariableEnvironment / LexicalEnvironment

 1) 둘 다 environmentRecord와 outerEnvironmentReference로 구성되며 VariableEnvironment는 최초 실행 시의 스냅샷을 유지한다는 점이 다름

 

 2) environmentRecord: 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장됨

  -컨텍스트를 구성하는 함수의 매개변수 식별자, 함수 자체, var로 선언된 식별자 등이 식별자에 해당

  -컨텍스트 내부 전체를 처음부터 끝까지 순서대로 훑어 수집

 

 3) 호이스팅: 변수 수집 과정을 이해하기 쉬운 방법으로 대체한 가상의 개념(자바스크립트가 식별자들을 컨텍스트의 최상단으로 끌어올린 후 실제 코드 실행)

  -예시 (1): 호이스팅은 다음과 같은 원리로 작동되지만 실제로 자바스크립트 실행과정에서 다음과 같이 코드가 바뀌지는 않음

// 01. 원본 코드

function a(x) {
  console.log(x);
  var x;
  console.log(x);
  var x = 2;
  console.log(x);
}

a(1);
// 02. 호이스팅(1) 매개변수를 함수 내에서 하나의 변수를 선언한 것과 같다고 가정

function a(){
  var x = 1;
  console.log(x);
  var x;
  console.log(x);
  var x = 2;
  console.log(x);
}

a();
// 03. 호이스팅(2) 식별자를 함수 가장 위로 끌어올린 뒤 나머지 코드 실행

function a() {
  var x;
  var x;
  var x;
  
  x = 1;
  console.log(x);  // 1
  console.log(x);  // 1
  x = 2;
  console.log(x);  // 2
}

a(1);

  -01에서 함수 a에 매개변수로 준 1는 함수 a 내부에서 var x = 1로 선언된 것과 같음

  -02에서 매개변수를 없애고 함수 a 내부에서 var x = 1을 선언하는 것으로 변경

  -03에서 var x를 선언한 모든 부분을 함수의 가장 위로 끌어올리고(호이스팅 하고) 나머지 코드 실행

  -03에서 끌어올린 var x선언 중 가장 처음을 제외한 나머지 두 부분은 중복되므로 무시됨

    →그 후, x = 1로 변수 x에 1이 할당되어 다음 두 줄에서 1이 출력

    →x = 2로 x에 2가 재할당되어 마지막 줄에서 2가 출력

 

  -예시(2): 컨텍스트 내에서 선언된 함수 자체도 하나의 식별자로 보고 호이스팅 하므로 다음과 같은 예시의 결과가 나옴

// 01. 원본 코드

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

a();
// 02. 변수 식별자롸 함수 호이스팅

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

a();
// 03. 함수 선언문을 함수 표현식으로 변경

function a() {
  var b;
  var b = function b() { }; // 호이스팅된 함수의 선언은 함수명으로 선언한 변수(b)에 함수를 할당한 것과 동일
  
  console.log(b);  // function b
  b = 'bbb';
  console.log(b);  // 'bbb'
  console.log(b);  // 'bbb'
}

a();

  -02에서 함수 a 내부의 모든 식별자 호이스팅(변수 식별자(var b), 함수 자체(function b()))

  -03에서 호이스팅된 함수 선언문을 선언한 변수(b)에 함수를 할당하는 것으로 변경

    →그러면 나머지 코드 실행 전에 변수 b에는 b함수가 할당되어 있고 첫 번째 코드 console.log(b)를 실행할 때는 함수 b가 출력

    →두 번째 코드에서 b = 'bbb'로 새로운 문자열이 할당되었으므로 원래 할당되어있던 함수는 삭제되고 b는 'bbb'가 됨

    →세 번째 코드부터는 b = 'bbb'로 할당되어 있으므로 'bbb' 출력

 

  ※ 함수 선언문과 함수 표현식

function a() {}  // 함수 선언문(함수명 a = 변수명)
a();                  // 실행 가능

var b = function () {}  // 익명 함수 표현식(변수명 b = 함수명)
b();                           // 실행 가능

var c = function d () {}  // 기명 함수 표현식(변수명 c != 함수명 d)
c();                              // 실행 가능
d();                             // 실행 불가능
// 함수 선언문과 함수 표현식의 호이스팅 방식 차이

console.log(sum(1, 2));
console.log(multiply(3, 4));

// 함수 선언문
function sum(a, b) {
  return a+b;
}

// 함수 표현식
var multiply(a,b) {
  return a*b;
}
// 함수 선언문은 전체를 호이스팅
var sum = function sum(a, b) {
  return a+b;
}

// 함수 표현식은 변수 선언부분만 호이스팅
var multiply

console.log(sum(1, 2));       // 3, 함수 선언문 전체가 호이스팅 되어 sum 함수가 선언된 상태이므로 정상적으로 sum 함수의 결과값이 반환
console.log(mutiply(3, 4));  // mutiply is not a function, 변수의 선언부분만 호이스팅되어 multiply가 함수가 아닌 선언되지 않은 변수의 상태이므로
                                        //아래에서 선언되는 multiply함수의 결과값을 윗부분인 이 곳에서 반환할 수 없음

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

 

 

 4) 스코프: 식별자에 대한 유효범위, 선언한 변수가 적용되는 범위

 5) 스코프 체인: 식별자의 유효범위를 안에서부터 바깥으로 검색해나가는 것

  -각 실행 컨텍스트가 활성화 될 때, LexiccalEnvironment(이하 L.E)가 생성되며, L.E 안에는 environmentRecord(이하 e), outerEnvironmentReference(이하 o)가 생성

  -outerEnvironmentReference는 현재 컨텍스트 바깥의 컨텍스트에 접근할 수 있도록 바깥의 컨텍스트를 저장

  -코드가 실행될 때 참조할 식별자는 현재 활성화 되어 있는 컨텍스트 내의 L.E에 먼저 접근

    →현재 활성화 컨텍스트의 L.E 내부에서 e를 먼저 확인하여 참조할 식별자가 있는지 확인

    →e에서 확인되지 않으면 o를 통해 바로 한 단계 위의 컨텍스트의 L.E 내부의 e를 확인

    →한 단계 위의 L.E의 e에도 참조할 식별자가 없다면 o를 통해 다시 그 위의 컨텍스트에 접근

    →위 과정 반복하며 전역 컨텍스트까지 참조할 식별자를 찾을 수 없으면 undefined

 

  -예시

var a = 1;

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

outer();

console.log(a);

   (1) 코드 시작과 함께 전역 컨텍스트 생성

        →전역 컨텍스트 L.E  -  e  -  { a = 1, outer(함수) }

 

   (2) outer 함수 호출로 전역 컨텍스트 비활성화, outer 실행 컨텍스트 활성화

        →outer 컨텍스트 L.E  -  e  -  { inner(함수) }

                                                      -  o  -  { GLOBAL L.E }

 

   (3) inner 함수 호출로 outer 컨텍스트 비활성화, inner 실행 컨텍스트 활성화

        →inner 컨텍스트 L.E  -  e  -  { a=3 }

                                                   -  o  -  { outer L.E }

        →inner 함수 내에서 호이스팅에 의해 var a;가 위로 올라가고 → console.log(a)에서는 a=3이 선언되기 전이므로 undefined 출력

        →inner 함수내에서 a = 3 선언

        →inner 함수의 마지막 코드 실행과 함께 inner 컨텍스트가 사라짐

    ※ 가장 마지막에 var a = 3;이 선언되어 있지 않다면 호이스팅되는 a도 없어서 외부 컨텍스트에서 a를 찾았겠지만 밑에서 a가 선언되어 undefined가 출력되어 버림

          (변수 은닉화: inner 변수 내부에 a 변수 선언으로 전역 공간의 변수 a에 접근할 수 없어짐)

 

   (4) outer 함수의 나머지 부분, console.log(a) 실행 → outer 컨텍스트의 L.E에는 식별자 a가 없으므로

          → o를 통해 GLOBAL L.E로 가서 → GLOBAL L.E의 e에 있는 식별자 a = 1을 받아와 1출력

 

   (5) outer 함수의 마지막 코드 실행과 함께 outer 컨텍스트가 사라짐

 

   (6) 전역 컨텍스트의 나머지 부분, console.log(a) 실행 → 전역 컨텍스트의 L.E의 e에 있는 식별자 a = 1을 받아와 1 출력

 

 6) 전역변수와 지역변수

  -위 예시에서 전역 스코프에서 선언한 a와 outer가 전역변수

  -outer 함수 내부에서 선언한 inner와 inner 함수 내부에서 선언한 a는 지역변수

  -지역변수는 함수 내부에서만 쓸 수 있으므로 전역 공간에서 접근 불가능 → 코드가 망가질 염려가 없음

  -전역변수의 사용을 최소화하는 것이 좋음

+ Recent posts