실행 컨텍스트 및 this

verdantgreeny·2025년 1월 7일

본캠프

목록 보기
19/56

1. 실행 컨텍스트란?

  • 실행할 코드에 제공할 환경 정보들을 모아놓은 객체
  • 해당 객체에는 변수 객체, 스코프 체인, this 정보가 담겨있다.
  • 동일 환경에 있는 코드를 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이것을 콜스택(Last in, first out)에 쌓아 올려 코드의 환경과 순서를 보장한다.

1-1. 실행 컨텍스트 객체의 실체

1) VariableEnvironment

  • 현재 컨텍스트 내의 식별자 정보(=environmentRecord)
  • 외부 환경 정보(=outerEnvironmentReference)
  • 선언 시점 LexicalEnvironment의 snapshot 유지O

2) LexicalEnvironment

  • environmentRecord(=record)
  • outerEnvironmentReference(=outer)
  • 선언 시점 LexicalEnvironment의 snapshot 유지X

3) ThisBinding

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

1-2. VE vs LE

1) VE와 LE의 차이점 : 스냅샷 유지여부

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

2) environmentRecord란

  • 현재 켄텍스트와 관련된 코드의 식별자 정보들이 저장
  • 함수에 지정된 배개변수 식별자, 함수자체, var로 선언된 변수 식별자 등

1-3. LE

1) Record

  • 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장(수집)
  • 수집 대상 정보 : 함수에 지정된 매개변수 식별자, 함수 자체, var로 선언된 변수 식별자 등
  • 컨텍스트 내부를 처음부터 끝까지 순서대로 훑어가며 수집(!주의! 순서대로 '수집'하는 것이지, 코드가 '실행'되는 것은 아니다)

2) 호이스팅

2-1) 변수 var는 선언부를 호이스팅한다.

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

//호이스팅적용

function a () {
	var x;
	var x;
	var x;

	x = 1;
	console.log(x);
	console.log(x);
	x = 2;
	console.log(x);
}
a(1);
  • 예상하기로는 1, undefined, 2가 나올거 같지만 실제로는 1, 1, 2라는 결과가 나온다.

2-2) 함수 선언은 전체를 호이스팅한다.

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

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

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

    console.log(b);
    console.log(b);
}
a();
  • 실행결과 에러, 'bbb', b함수를 예상했지만 실제로는 b함수, 'bbb', 'bbb'가 나온다.

2-3) 함수 선언문 vs 함수 표현식 : 되도록 함수 표현식을 쓰자!!

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



//호이스팅 적용
// 함수 선언문은 전체를 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;
};

3) 스코프 체인과 outerEnvironmentReference

3-1) 용어정리

  • 스코프 : 식별자에 대한 유효범위를 의미
  • 스코프 체인 : 식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해나가는 것
  • outerEnvironmentReference : 스코프 체인이 가능토록 하는 것(외부 환경의 참조정보)

3-2) 스코프체인

  • outer는 현재 호출된 함수가 선언될 당시(이 말이 중요해요!)의 LexicalEnvironment를 참조
  • 항상 outer는 오직 자신이 선언된 시점의 LexicalEnvironment를 참조하고 있으므로, 가장 가까운 요소부터 차례대로 접근 가능
  • 결론 : 무조건 스코프 체인 상에서 가장 먼저 발견된 '식별자'에게만 접근이 가능

4) 최종결론

각각의 실행 컨텍스트는 LE 안에 record와 outer를 가지고 있고, outer 안에는 그 실행 컨텍스트가 선언될 당시의 LE정보가 다 들어있으니 scope chain에 의해 상위 컨텍스트의 record를 읽어올 수 있다.

2. this란?

  • 다른 객체지향 언어 : 클래스로 생성한 인스턴스
  • 자바스크립트의 this는 어디에서나 사용가능
  • 자바스크립트의 this는 함수를 호출할 때 결정

2-1. 상황에 따른 this

1) 전역공간에서 호출

  • 전역 공간에서 this는 전역 객체를 가리킴
  • 런타임 환경에 따라 this는 window(브라우저 환경) 또는 global(node 환경)를 각각 가리킴

2) 메서드로서 호출

  • 함수 : 그 자체로 독립적인 기능을 수행
  • 메서드 : 자신을 호출한 대상 객체에 대한 동작을 수행
// CASE1 : 함수
// 호출 주체를 명시할 수 없기 때문에 this는 전역 객체를 의미
var func = function (x) {
	console.log(this, x);
};
func(1); // Window { ... } 1

// CASE2 : 메서드
// 호출 주체를 명시할 수 있기 때문에 this는 해당 객체(obj)를 의미
// obj는 곧 { method: f }를 의미
var obj = {
	method: func,
};
obj.method(2); // { method: ƒ } 2

3) 함수로서 호출

3-1) 함수 내부에서의 this
: 함수로서 ‘독립적으로’ 호출할 때this는 항상 전역객체를 가리킴
3-2) 메서드의 내부함수에서의 this
: 메서드의 내부라고 해도, 함수로서 호출한다면 this는 전역 객체를 의미
3-3) 메서드의 내부 함수에서의 this 우회

  • 변수를 활용하는 방법 : 내부 스코프에 이미 존재하는 this를 별도의 변수(ex : self)에 할당하는 방법
  • 화살표 함수(=this를 바인딩하지 않는 함수) : ES6에서 처음 도입된 화살표 함수는, 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 없어 this는 이전의 값-상위값-이 유지

4) 콜백 함수 호출

: 콜백함수 내부의 this는 해당 콜백함수를 넘겨받은 함수(메서드)가 정한 규칙에 따라 값이 결정

  • setTimeout 함수, forEach 메서드는 콜백 함수를 호출할 때 대상이 될 this를 지정하지 않으므로, this는 곧 window객체
  • addEventListner 메서드는 콜백 함수 호출 시, 자신의 this를 상속하므로, this는 addEventListner의 앞부분(button 태그)

5) 생성자 함수

  • 생성자 : 구체적인 인스턴스(어려우면 객체로 이해!)를 만들기 위한 일종의 틀
var Cat = function (name, age) {
	this.bark = '야옹';
	this.name = name;
	this.age = age;
};

var choco = new Cat('초코', 7); //this : choco
var nabi = new Cat('나비', 5);  //this : nabi

2-2. 명시적 this 바인딩

1) call 메서드

  • 호출 주체인 함수를 즉시 실행하는 명령어
  • call명령어를 사용하여, 첫 번째 매개변수에 this로 binding할 객체를 넣어주면 명시적으로 binding
var func = function (a, b, c) {
	console.log(this, a, b, c);
};

// no binding
func(1, 2, 3); // Window{ ... } 1 2 3

// 명시적 binding
// func 안에 this에는 {x: 1}이 binding돼요
func.call({ x: 1 }, 4, 5, 6}; // { x: 1 } 4 5 6

2) apply 메서드

  • call 메서드와 매우 비슷
  • 차이점은 this에 binding할 객체는 똑같이 넣어주고 나머지 부분만 배열 형태로 넘겨줌
var func = function (a, b, c) {
	console.log(this, a, b, c);
};
func.apply({ x: 1 }, [4, 5, 6]); // { x: 1 } 4 5 6

var obj = {
	a: 1,
	method: function (x, y) {
		console.log(this.a, x, y);
	}
};

obj.method.apply({ a: 4 }, [5, 6]); // 4 5 6

3) bind 메서드

  • call과는 다르게 즉시 호출하지는 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하는 메서드
  • 목적
    1. 함수에 this를 미리 적용
    2. 부분 적용 함수 구현할 때 용이
   var func = function (a, b, c, d) {
	console.log(this, a, b, c, d);
};
func(1, 2, 3, 4); // window객체

// 함수에 this 미리 적용
var bindFunc1 = func.bind({ x: 1 }); // 바로 호출되지는 않아요! 그 외에는 같아요.
bindFunc1(5, 6, 7, 8); // { x: 1 } 5 6 7 8

// 부분 적용 함수 구현
var bindFunc2 = func.bind({ x: 1 }, 4, 5); // 4와 5를 미리 적용
bindFunc2(6, 7); // { x: 1 } 4 5 6 7
bindFunc2(8, 9); // { x: 1 } 4 5 8 9

0개의 댓글