[JavaScript][TIL] 실행 컨텍스트( 스코프, 변수, 객체, 호이스팅)

Trippy·2023년 10월 21일
0

JavaScript

목록 보기
11/28
post-thumbnail

실행 컨텍스트(스코프, 변수, 객체, 호이스팅)

자바스크립트의 실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 다음과 같은 일을 한다.

  1. 선언된 변수를 위로 끌어올린다 (hoisting)
  2. 외부 환경 정보를 구성한다.
  3. this값을 설정한다.

[1] 실행 컨텍스트란?

실행 컨텍스트를 이해하기 위해서는 Call stack을 이해해야 한다.

  1. 스텍 vs 큐

  2. 콜 스텍(call stack)

    실행 컨텍스트란 실행할 코드에 제공할 환경 정보들을 모아놓은 객체라고 한다.
    동일 환경에 있는 코드를 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이것을 위해서 설명한 '스텍'의 한 종류인 콜스텍이 쌓아 올린다. 가장 위에 쌓여있는 컨텍스트와 관련된 코드를 실행하는 방법으로 코드의 환경 및 순서를 보장할 수 있다.

  3. 컨텍스트의 구성

    a. 구성방법

    1. 전역공간
    2. eval() 함수
    3. 함수

    b. 실행컨텍스트 구성 예시 코드

    // ---- 1번
    var a = 1;
    function outer() {
    	function inner() {
    		console.log(a); //undefined
    		var a = 3;
    	}
    	inner(); // ---- 2번
    	console.log(a);
    }
    outer(); // ---- 3번
    console.log(a);

    c. 실행컨텍스트 구성 순서
    위 코드는 아래 순서로 진행된다.

    코드실행 → 전역(in) → 전역(중단) + outer(in) → outer(중단) + inner(in) → inner(out) + outer(재개) → outer(out) + 전역(재개) → 전역(out) → 코드종료

    d. 결국 특정 실행 컨텍스트가 생성되듯 시점이 콜 스택의 맨 위에 쌓이는(노출되는) 순간을 의미한다, 곧, 현재 실행할 코드에 해당 실행 컨텍스트가 관여하게 되는 시점을 의미한다고 받아드릴 수 있다.

실행 컨텍스트 객체의 실체 (= 담기는 정보)

1. VariableEnvironment

현재 컨텍스트 내의 식별자 정보(=record)를 갖고 있다.

  • var a = 3;
  • 위의 경우 var a를 의미

외부 환경 정보(=outer)를 갖고 있다.

선언 시점 LexicalEnvironment의 snapshot

2. LexicalEnvironment

  • VariableEnvironment와 동일하지만, 변경사항을 실시간으로 반영한다.

3. ThisBinding

  • this 식별자가 바라봐야 할 객체

[2] VariableEnvironment, LexicalEnvironment의 개요

VE vs LE

이 두가지는 담기는 항목은 완벽하게 동일하다, 그러나 스냅샷 유지여부는 다음과 같이 다르다

  1. VE: 스냅샷을 유지한다.
  2. LE: 스냅샷을 유지하지 않는다, 즉 실시간으로 변경사항을 계속해서 반영한다.
    => 결국 실행 컨텍스트를 생성할 때 VE에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LE를 만들고 이후에는 주로 LE를 사용한다.

구성 요소 (VE, LE 서로 같다)

  1. VE, LE 모두 동일하며, environmentRecordouterEnvironmentReference로 구성
  2. environmentRecord(=record)
    • 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다.
    • 함수에 지정된 매개변수 식별자, 함수자체, var로 선언된 변수 식별자 등
  3. outerEnvironmentReference(=outer)

[3] LexicalEnvironment(1) - environmentRecord(=record)와 호이스팅

개요

  1. 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다, 기록된다고 이해하고 보면 record라는 말과 일맥상통한다.
  2. 수집 대상 정보 : 함수에 지정된 매개변수 식별자, 함수 자체, var로 선언된 변수 식별자 등
  3. 컨텍스트 내부를 처음부터 끝까지 순서대로 훑어가며 수집

    순서대로 수집한다고 했지, 코드가 실행된다고 하지는 않음!

호이스팅

  1. 변수정보 수집을 모두 마쳤더라도 아직 실행 컨텍스트가 관여할 코드는 실행 전의 상태다(js엔진은 코드 실행 전 이미 모든 변수정보를 알고 있는 것)
  2. 변수 정보 수집 과정을 이해하기 쉽게 설명한 "가상 개념"

    가상개념이라는 말은 실제로는 그렇진 않더라도 사람이 이해하기 쉬운 말로 풀어 표현했다는 것을 의미한다.

호이스팅 규칙

1. 호이스팅 법칙1 : 매개변수 및 변수는 선언부를 호이스팅 한다.

다음 주석에 달려있는 3가지 action point에 따라 실습을 진행한다.

적용 전

/// 적용 전
 // action point 1: 매개변수 다시 쓰기(js엔진은 똑같이 이해한다)
 // action point 2: 결과 예상하기
 // action point 3: 호이스팅 적용해본 후 결과를 다시 예상해보기

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

매개변수 적용

// 매개변수 적용
//action point 1 : 매개변수 다시 쓰기(JS 엔진은 똑같이 이해한다)
//action point 2 : 결과 예상하기
//action point 3 : hoisting 적용해본 후 결과를 다시 예상해보기

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

호이스팅 적용

// 호이스팅 적용
//action point 1 : 매개변수 다시 쓰기(JS 엔진은 똑같이 이해한다)
//action point 2 : 결과 예상하기
//action point 3 : hoisting 적용해본 후 결과를 다시 예상해보기

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);

2. 호이스팅 규칙2 : 함수 선언은 전체를 호이스팅합니다.

적용 전

//action point 1 : 결과 값 예상해보기
//action point 2 : hoisting 적용해본 후 결과를 다시 예상해보기

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

호이스팅 적용

//action point 1 : 결과 값 예상해보기
//action point 2 : hoisting 적용해본 후 결과를 다시 예상해보기

function a () {
	var b; // 변수 선언부 호이스팅
	function b() { } // 함수 선언은 전체를 호이스팅

	console.log(b);
	b = 'bbb'; // 변수의 할당부는 원래 자리에

	console.log(b);
	console.log(b);
}
a();

함수선언문을 함수 표현식으로

//action point 1 : 결과 값 예상해보기
//action point 2 : hoisting 적용해본 후 결과를 다시 예상해보기

function a () {
	var b; // 변수 선언부 호이스팅
	var b = function b() { } // 함수 선언은 전체를 호이스팅

	console.log(b); // function b
	b = 'bbb'; // 변수의 할당부는 원래 자리에

	console.log(b); // bbb
	console.log(b); // bbb
}
a();

[3] 함수 선언문, 함수 표현식

1. 함수 정의의 3가지 방식

// 함수 선언문. 함수명 a가 곧 변수명
// function 정의부만 존재, 할당 명령이 없는 경우
function a () { /* ... */ }
a(); // 실행 ok

// 함수 표현식. 정의한 function을 별도 변수에 할당하는 경우
// (1) 익명함수표현식 : 변수명 b가 곧 변수명(일반적 case에요)
var b = function () { /* ... */ }
b(); // 실행 ok

// (2) 기명 함수 표현식 : 변수명은 c, 함수명은 d
// d()는 c() 안에서 재귀적으로 호출될 때만 사용 가능하므로 사용성에 대한 의문
var c = function d () { /* ... */ } 
c(); // 실행 ok
d(); // 에러!

2. 주의해야 할 내용

  1. 함수 선언문, 함수 표현식

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

  • 그 객체 안에는 3가지가 존재한다.
    ✓ VariableEnvironment
    ✓ LexicalEnvironment
    ✓ ThisBindings
  • VE와 LE는 실행컨텍스트 생성 시점에 내용이 완전히 같고, 이후 스냅샷 유지 여부가 다르다.
  • LE는 다음 2가지 정보를 가지고 있다.
    ✓ record(=environmentRecord) ← 이 record의 수집과정이 hoisting
    ✓ outer(=outerEnvironmentReference)

함수 선언문과 표현식을 배웠으니, 실질적인 차이를 예시를 통하면

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

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

var multiply = function (a, b) { // 함수 표현식 multiply
	return a + b;
}

LE는 record와 outer를 수집한다, 그 중 record를 수집하는 과정에서 호이스팅이 일어나고, 우리가 알고 있는대로 위로 쭉 끌어올려본 결과를 다시 써보면 아래와 같다.

// 함수 선언문은 전체를 hoisting
function sum (a, b) { // 함수 선언문 sum
	return a + b;
}

// 변수는 선언부만 hoisting

var multiply; 

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

multiply = function (a, b) { // 변수의 할당부는 원래 자리
	return a + b;
};

함수 선언문과 표현식은 호이스팅 과정에서 극명한 차이를 보인다.

  1. 함수 선언문을 주의해야 하는 이유
...

console.log(sum(3, 4));

// 함수 선언문으로 짠 코드
// 100번째 줄 : 시니어 개발자 코드(활용하는 곳 -> 200군데)
// hoisting에 의해 함수 전체가 위로 쭉!
function sum (x, y) {
	return x + y;
}

...
...

var a = sum(1, 2);

...

// 함수 선언문으로 짠 코드
// 5000번째 줄 : 신입이 개발자 코드(활용하는 곳 -> 10군데)
// hoisting에 의해 함수 전체가 위로 쭉!
function sum (x, y) {
	return x + ' + ' + y + ' = ' + (x + y);
}

...

var c = sum(1, 2);

console.log(c);
  1. 함수 표현식이었다면?
...

console.log(sum(3, 4));

// 함수 표현식으로 짠 코드
// 함수 선언부만 위로 쭉!
// 이 이후부터의 코드만 영향을 받아요!
var sum = function (x, y) {
	return x + y;
}

...
...

var a = sum(1, 2);

...

// 함수 표현식으로 짠 코드
// 함수 선언부만 위로 쭉!
// 이 이후부터의 코드만 영향을 받아요!
var sum = function (x, y) {
	return x + ' + ' + y + ' = ' + (x + y);
}

...

var c = sum(1, 2);

console.log(c);

협업을 많이 하고 복잡한 코드 일수록, 전역 공간에서 이루어지는 코드 협업 일수록 함수 표현식을 활용하는 습관을 들이도록 하자.

profile
감금 당하고 개발만 하고 싶어요

0개의 댓글