코어자바스크립트 02.실행컨텍스트

실행컨텍스트

  • 실행할 코드에 제공할 환경 정보들을 모아놓은 객체

스택(Stack), 큐 (Queue)

  1. Stack
  • 나중에 집어넣은 데이터가 먼저 나옵니다. LIFO(Last In First Out)
  • 데이터를 집어넣는 push, 데이터를 추출하는 pop
  1. Queue
  • 먼저 집어넣은 데이터가 먼저 나옵니다. FIFO(First In First Out)
  • 데이터를 집어넣는 enqueue, 데이터를 추출하는 dequeue

실행컨텍스트 동작

  • 동일한 환경에 있는 코드들을 실행할때 필요한 환경 정보들을 모아 컨텍스트 구성
  • 컨텍스트들을 순서대로 Call Stack에 쌓아올림
  • 최상위 컨텍스트와 관련있는 코드들을 실행
  • 전체 코드의 환경과 순서 보장

실행 컨텍스트의 구성 방법

  • 전역공간 -> 자동실행
  • 함수 -> 가장 흔한 실행 컨텍스트 구성방법
  • 블록 -> { }으로 둘러 쌓인 코드내부 ES6부터 지원

(예제 2-1) 실행 컨텍스트와 콜스택

1
2
3
4
5
6
7
8
9
10
11
//(1) 전역컨텍스트가 콜스텍에 담긴다
function outer() {
  function inner() {
    console.log(a); //undefined
    var a = 3;
  }
  inner(); //(2)
  console.log("inner", a);
}
outer(); //(3) outer 실행컨텍스트를 생성한 후 콜 스택에 담는다.
console.log("outer", a);}
  • 실행순서: (1) -> (3) -> (2)

콜스택 변화

활성화된 실행 컨텍스트의 수집정보

  • VariableEnvironment
    • environmentRecord (snapshot)
    • outerEnvironmentReference (snapshot)
  • LexicalEnvironment
    • environmentRecord
    • outerEnvironmentReference
  • ThisBinding

VariableEnvironment

  • 담기는 내용은 LexicalEnvironment와 같지만 최초 실행 시의 스냅샷을 유지
  • 실행 컨텍스트 생성 시 VariableEnvironment에 먼저 정보를 담고, 그대로 LexicalEnvironment에 복사 한 후 주로 LexicalEnvironment를 활용
  • 내부 구성 요소 : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보가 선언 시점의 스냅샷으로 저장
    • environmentRecord (snapshot)
    • outerEnvironmentReference (snapshot)

LexicalEnvironment

  • 정의
    • 한국어로 번역 시 ‘어휘적 환경’, ‘정적 환경’을 사용하는데 모두 적절치 않음
    • 현재 컨텍스트 내부에는 a, b, c와 같은 식별자들이 있고, 그 외부 정보는 D를 참조하도록 구성
    • 위와 같은 구성이 마치 사전과 비슷하여 ‘사전적 환경’이 더 적합한 표현
    • 하지만 그냥 의미만 이해하고 지칭할 때는 LexicalEnvironment로 사용하는 것이 가장 좋음
  • 내부 구성 요소 : VariableEnvironment와 동일하지만 변경 사항이 실시간으로 반영 됨
    • environmentRecord
    • outerEnvironmentReference

environmentRecord와 호이스팅(hoisting)

  1. environmentRecord
  • 현재 컨텍스트와 관련 된 코드의 식별자 정보들이 저장
    • 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자
    • 선언한 함수가 있을 경우 그 함수 자체
    • var로 선언 된 변수의 식별자
  • 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집
  • 전역 실행 컨텍스트
    • 변수 객체 생성 대신 자바스크립트 구동 환경이 별도로 제공하는 전역 객체를 활용
    • 브라우저의 window, Node.js의 global 객체 등
    • 이들은 내장(native) 객체가 아닌 호스트(host) 객체로 분류
  1. 호이스팅의 개념
  • 호이스팅(hoisting) : hoist (끌어 올리다) + ~ing → 식별자들을 최상단으로 끌어 올려 놓자
  • 변수 정보를 수집하는 과정을 더욱 이해하기 쉬운 방법으로 대체한 가상의 개념
  • 자바스크립트 엔진이 실제로 끌어 올리지는 않지만 편의상 끌어 올리는 것으로 간주하자는 것

2-3 호이스팅 규칙 - 매개 변수와 변수 선언에 대한 호이스팅

  • environmentRecord는 현재 실행 될 컨텍스트 대상 코드 내에 어떤 식별자들이 있는지에만 관심이 있지, 각 식별자들이 어떤 값이 할당 될지에는 관심이 없음
1
2
3
4
5
6
7
8
9
10
function a(x) {
  // 수집 대상 1 (매개 변수)
  console.log(x); // (1)
  var x; // ------- 수집 대상 2 (변수 선언)
  console.log(x); // (2)
  var x = 2; // --- 수집 대상 3 (변수 선언)
  console.log(x); // (3)
}

a(1);

예제 2-3 결과확인

예제 2-4 변수 선언 부분과 할당 부분을 나눠 호이스팅

1
2
3
4
5
6
7
8
9
10
11
12
function a() {
  var x; // -------- 수집 대상 1(매개 변수)의 변수 선언 부분
  var x; // -------- 수집 대상 2의 변수 선언 부분
  var x; // -------- 수집 대상 3의 변수 선언 부분
  x = 1; // -------- 수집 대상 1의 할당 부분
  console.log(x); // (1)
  console.log(x); // (2)
  x = 2; // -------- 수집 대상 3의 할당 부분
  console.log(x); // (3)
}

a();
  • environmentRecord는 현재 실행 될 컨텍스트 대상 코드 내에 어떤 식별자들이 있는지에만 관심이 있기 때문에 변수 선언 부분만 최상단으로 올라가게 됨 예제 2-4 결과확인

2-5 호이스팅 규칙 - 함수 선언에 대한 호이스팅

1
2
3
4
5
6
7
8
function a() {
  console.log(b); // (1)
  var b = "yurihaha"; // 수집 대상 1 (변수 선언)
  console.log(b); // (2)
  function b() {} // 수집 대상 2 (함수 선언)
  console.log(b); // (3)
}
a();

예제 2-5 결과확인

  • a 함수를 실행하는 순간 a 함수의 실행컨텍스트 생성 (변수명, 함수선언 정보 위로 끌어올림)
  • 변수는 선언부와 할당부를 나누어 선언부만 끌어올리는 반면 함수선언은 함수 전체를 끌어올린다.
  • 수집대상 1, 수집대상 2를 순서대로 끌어올린다.

2-6 함수 선언의 호이스팅(2) 호이스팅을 마친상태

1
2
3
4
5
6
7
8
9
10
function a() {
  var b; // ------------------- 수집 대상 1(변수 선언) 의 선언 부분
  function b() {} // ---------- 수집 대상 2(함수 선언) 의 전체
  // var b = function b() {} // (*) 위의 코드를 이렇게 표현도 가능
  console.log(b); // ---------- (1)
  b = "bbb"; // --------------- 수집 대상 1의 할당 부분
  console.log(b); // ---------- (2)
  console.log(b); // ---------- (3)
}
a();

예제 2-6 결과확인

함수 선언문과 함수 표현식

  • 함수 선언문 : 함수 정의부만 존재하고 별도의 할당 명령이 없는 것
    • 호이스팅 시 함수 전체를 끌어 올림
  • 함수 표현식 : 정의한 함수를 별도의 변수에 할당하는 것
    • 익명 함수 표현식 : 함수명을 정의하지 않음
    • 기명 함수 표현식 : 함수명을 정의함
      • 외부에서 함수명으로 호출 불가능
      • 내부에서 재귀함수를 호출하는 용도로 사용. 하지만 변수명으로도 호출 가능하여 굳이 쓸 필요는 없어 보임
      • 호이스팅 시 변수로 취급되어 선언 부분만 끌어 올림
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function a() {
  /* ... */
} // 함수 선언문. 함수명 a가 곧 변수명
a(); // 실행 가능

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

var c = function d() {
  /* ... */
}; // (기명) 함수 표현식. 변수명은 c, 함수명은 d
c(); // 실행 가능
d(); // 실행 불가능. 에러!

함수 선언문의 위험성

  • 함수 선언문이 코드 상단에 선언되어 있는 상태에서 동일한 이름의 함수 선언문이 코드 하단에 중복해서 선언 될 경우.
  • 호이스팅 과정에서 두 함수 선언문이 모두 끌어 올려지고, 상대적으로 하단에 있는 함수 선언문이 코드 전체에 영향을 미치게 됨.
  • 함수 표현식으로 함수를 사용한다면 하단의 함수가 나오기 전까지는 상단에 선언 된 함수로 사용 되다가 하단의 함수가 나오면 그때 부터 하단의 함수로 적용 됨.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
console.log(sum(3, 4)); // (1) : 7을 기대하지만, 3 + 4 = 7이 출력

function sum(x, y) {
  // -- 상단 함수
  return x + y;
}

var a = sum(1, 2);
console.log(a); // ------- (2) : 3을 기대하지만, 1 + 2 = 3이 출력

function sum(x, y) {
  // -- 하단 함수
  return `${x} + ${y} = ${x + y}`;
}

var c = sum(1, 2);
console.log(c); // ------- (3) : 1 + 2 = 3이 출력

예제 2-7 결과확인

함수 표현식

  • 의도대로 함수가 잘 작동되기위해서 상대적으로 함수 표현식이 안전하다!
  • 그 줄아에서 바로 에러가 검출되므로 빠른 타이밍에 손쉽게 디버깅도 가능하기 때문
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// console.log(sum(3, 4)); // (1) : sum의 값이 없어서 undefined를 반환하기 때문에 에러!

var sum = function (x, y) {
  // -- 상단 함수
  return x + y;
};

var a = sum(1, 2);
console.log(a); // ------- (2) : 3 출력

var sum = function (x, y) {
  // -- 하단 함수
  return `${x} + ${y} = ${x + y}`;
};

var c = sum(1, 2);
console.log(c); // ------- (3) : 1 + 2 = 3이 출력

스코프, 스코프 체인, outerEnvironmentReference

  • 스코프 (Scope) : 식별자에 대한 유효 범위
    • ES5까지는 전역 공간 외의 오직 함수에 의해서만 스코프가 생성
  • 스코프 체인 (Scope chain) : 식별자에 대한 유효 범위를 안에서 부터 바깥으로 차례로 검색해 나가는 것
    • 스코프 체인을 가능케 하는 것이 outerEnvironmentReference

스코프체인

  • 현재 스코프에 선언 된 식별자가 없으면 상위 스코프로 이동하여 검색
  • 여러 스코프에서 동일한 식별자를 선언한 경우에는 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능
  • 변수 은닉화 (Variable shadowing) : 상위 스코프에 선언되어 있지만, 현재 스코프에 이미 선언된 경우에는 값이 할당되어 있지 않다고 하더라도 현재 스코프의 변수를 우선으로 함
1
2
3
4
5
6
7
8
9
10
11
var a = 1;
var outer = function () {
  var inner = function () {
    console.log(a); // (1)
    var a = 3;
  };
  inner();
  console.log(a); // - (2)
};
outer();
console.log(a); // --- (3)

스코프체인-크롬전용

1
2
3
4
5
6
7
8
9
10
var a = 1;
var outer = function () {
  var b = 2;
  var inner = function () {
    // console.log(b); //
    console.dir(inner);
  };
  inner();
};
outer();

전역변수와 지역변수

  • 전역변수: 전역스코프에서 선언한 변수 (a, outer)
  • 지역변수는 함수 내부에 선언한 변수 (outer내부에 선언한 inner, inner함수에 선언한 함수)