실행 컨텍스트(Execution Context)

G_NooN·2024년 1월 27일
0

JavaScript

목록 보기
4/7

실행 컨텍스트(Execution Context)란?

실행 가능한 코드의 모든 환경 정보를 모아놓은 객체

구성 요소

  • 실행 컨텍스트(Execution Context)
    • VE(Variable Environment) / LE(Lexical Environment)
      • Environment Record
      • Outer Environment Reference
    • This Binding Component

VE(Varibale Environment) vs LE(Lexical Environment)

var a = 3

  • 공통점
    • 식별자 정보(Environment Record)를 가짐 (ex. var a)
    • 외부 환경 정보(Outer Environment Reference)를 가짐
  • 차이점
    • VE : 변수가 생성된 그 순간의 정보(Snapshot)만 기억함
    • LE : 변수의 정보가 실시간으로 업데이트가 가능한 VE

Environment Record

  • 현재 context와 관련된 코드의 식별자 정보를 저장함
    (함수에 지정된 매개변수 식별자, 함수 자체, 변수 식별자 등)

Outer Environment Refernce

  • 함수가 선언되는 시점의 외부 환경 정보를 가짐

생성/활성화 시점

  • 콜 스택(Call Stack)의 가장 상단에 쌓이는 시점

호이스팅(Hoisting)

JS 엔진이 자체적으로 변수/함수의 Environment Record를 코드의 최상단으로 올려 해석하는 것

변수의 호이스팅

  • 변수의 선언부(ex. var a = 3var a)를 끌어올림
  // TEST 코드
  function a(x) {
    console.log(x);
    var x;
    console.log(x);
    var x = 2;
    console.log(x);
  }
  a(1);

  // 매개변수를 적용한 함수의 내부
  var x = 1;
  console.log(x);
  var x;
  console.log(x);
  var x = 2;
  console.log(x);

  // 호이스팅을 적용한 함수의 내부
  var x;
  var x;
  var x;

  x = 1;
  console.log(x);	// 1
  console.log(x);	// 1
  x = 2;
  console.log(x);	// 2

함수의 호이스팅

  • 함수 전체를 끌어올림
  // TEST 코드
  function a() {
    console.log(b);
    var b = 'bbb';
    console.log(b);
    function b() {}
    console.log(b);
  }
  a();

// 호이스팅을 적용한 함수의 내부
  var b;
  function b() {}
  
  console.log(b);	// [Function: b]
  b = 'bbb';
  console.log(b);	// bbb
  console.log(b);	// bbb

주의할 점 (함수 선언문 vs 함수 표현식(=함수 리터럴))

함수 선언문
  • 함수 전체가 호이스팅 되기 때문에 아래의 sum 함수가 위의 sum 함수를 덮어쓴다.
  // TEST 코드
  function sum (a, b) {
    return a + b;
  }
  console.log(sum(1, 2));

  function sum (a, b) {
    return 'a: ' + a + ', b: ' + b;
  }
  console.log(sum(1, 2));
  // 호이스팅 결과
  function sum (a, b) {
    return a + b;
  }
  function sum (a, b) {
    return 'a: ' + a + ', b: ' + b;
  }

  console.log(sum(1, 2));	// a: 1, b: 2
  console.log(sum(1, 2));	// a: 1, b: 2
함수 표현식
  • 변수의 선언부가 호이스팅 되고 그 이후에 변수에 함수가 할당되기 때문에 아래의 sum 함수가 위의 sum 함수를 덮어쓰지 않는다.
  // TEST 코드
  var sum = function (a, b) {
    return a + b;
  }

  console.log(sum(1, 2));

  var sum = function (a, b) {
    return 'a: ' + a + ', b: ' + b;
  }

  console.log(sum(1, 2));
  // 호이스팅 결과
  var sum;
  var sum;

  sum = function (a, b) {
    return a + b;
  }

  console.log(sum(1, 2));	// 3

  sum = function (a, b) {
    return 'a: ' + a + ', b: ' + b;
  }

  console.log(sum(1, 2));	// a: 1, b: 2

식별자의 영향 범위(Scope)

종류영향 범위
전역 Scope프로그램 전체에 영향을 줌
지역 Scope함수의 내부에만 영향을 줌
블록 Scope블록의 내부에만 영향을 줌(ex. if문, for문)

Outer Environment Reference

Outer Environment Reference는 함수가 선언되는 시점의 외부 환경 정보를 가진다.

중첩 함수에서의 Outer Environment Reference

  • 각 중첩 함수는 선언될 때마다 콜 스택(Call Stack)에 적재되며, 실행 완료 시 콜 스택(Call Stack)에서 빠져나간다.

  • 함수의 식별자 정보를 찾는 과정

    • 자신의 내부의 식별자 정보를 탐색
    • 자신의 내부에 정보가 존재하지 않는 경우, Outer Environment Reference를 통해 한 단계 상위 함수에서 식별자 정보를 탐색
    • 해당 함수에도 존재하지 않을 경우, 해당 함수의 Outer Environment Reference를 통해 한 단계 상위 함수에서 식별자 정보를 탐색
    • (반복)
  • 예시

  // TEST 코드
  var a = 1;
  var outer = function() {
    var inner = function() {
      console.log(a);	// 1번 a
      var a = 3;
    };
    inner();
    console.log(a);	// 2번 a
  };
  outer();
  console.log(a);		// 3번 a
1번 a 탐색 과정
  • inner Function의 호이스팅 결과
  var a;
  console.log(a);	// undefined
  a = 3;

따라서, 1번 a = undefined

2번 a 탐색 과정
  • 2번 a는 inner()를 사용한 이후의 a 이기 때문에, inner 함수의 a 값은 외부에 영향을 주지 않는다.
    (콜 스택에서 빠져나감)
  // inner() 함수 실행 이후의 코드
  var a = 1;
  var outer = function() {
    console.log(a);	// 2번 a
  };
  outer();
  console.log(a);	// 3번 a
  • outer 함수의 내부에는 a 값이 존재하지 않으므로, Outer Environment Reference를 통해 가장 가까운 외부에서 a 값을 찾는다. var a = 1

따라서, 2번 a = 1

3번 a 탐색 과정
  • 3번 a는 outer()를 사용한 이후의 a이기 때문에, outer 함수의 a 값은 외부에 영향을 주지 않는다.
    (콜 스택에서 빠져나감)
  // outer() 함수 실행 이후의 코드
  var a = 1;
  console.log(a);	// 3번 a

따라서, 3번 a = 1

클로저(Closure)

중첩 함수가 해 함수의 외부 함수보다 더 오래 유지되는 경우, 외부 함수가 종료되었음에도 중첩 함수가 Outer Environment Reference를 통해 변수의 식별자 정보를 참조할 수 있음

  • 이유 : 변수는 자신이 선언되는 시점의 Lexical Environment를 가지기 때문

  • 예시

  // TEST 코드
  const x = 1;
  
  function outer() {
    const x = 10;
    const inner = function() {
      console.log(x);
    };
    return inner;
  }
  
  const innerFunc = outer();
  innerFunc();
  1. outer 함수의 구조
  • outer 함수에서 x를 10으로 선언한다.
  • 중첩 함수인 inner 함수에서 x를 출력한다.
  • 중첩 함수인 inner 함수를 return 한다.
  1. const innerFunc = outer();
  • innerFunc는 outer 함수의 수행 결과값을 저장한다.
  • outer 함수는 중첩 함수인 inner 함수를 사용한다.
  • inner 함수는 Lexical Environment로 x = 10을 저장한다.
  • outer 함수는 inner 함수의 결과값(x = 10)을 return 하고 종료하며 콜 스택에서 빠져 나온다.
  • 하지만, inner 함수가 여전히 outer 함수의 x값을 참조하고 있기 때문에,
    Lexical Environment인 x = 10은 outer 함수 종료 이후에도 유효하다.

따라서, innerFunc()의 결과값 = 10

클로저의 사용 용도
: 상태(state)가 외부에 의해 의도치 않게 변경되지 않도록 하기 위해 사용 (캡슐화: Encapsulation)


This Binding

this 값 : 함수가 호출되는(=실행 컨텍스트가 생성되는) 시점에 해당 함수가 속한 객체의 참조 값

상황에 따른 this 값

this 값은 호출되는 상황에 따라 값이 변경된다.

상황this 값
최상위 레벨(전역 공간)의 thisglobal(node.js), window(브라우저)
함수 내부의 thisglobal(node.js), window(브라우저)
메서드 내부의 this메서드를 호출한 객체
메서드 내부의 함수의 thisglobal(node.js), window(브라우저)
이벤트 핸들러의 this이벤트를 호출한 요소
화살표 함수의 this함수의 한 단계 상위 객체

명시적 this binding

메서드를 사용하여 this가 가리키는 객체를 지정할 수 있음

call/apply 메서드

  • 공통점 : 호출할 때 this의 값을 지정하고, 함수를 즉시 실행함
  • 차이점
    • call : this 값에 추가할 인수를 쉼표(,)로 구분하여 입력함
    • apply : this 값에 추가할 인수를 배열[] 형태로 입력함
  let obj = {
    a: 1,
    method: function(x, y) {
      console.log(this.a, x, y);
    }
  };

  obj.method(2, 3);					// 1 2 3
  obj.method.call({a: 4}, 5, 6);		// 4 5 6
  obj.method.apply({a: 7}, [8, 9]);	// 7 8 9
  • 활용 분야
  1. 유사 배열 객체(Array-like Object)

유사 배열 객체
: 인덱스(index)와 길이(length)가 존재하는 객체

  • 특징
    • length를 반드시 객체에 명시해야 한다.
    • 배열의 메서드를 직접 사용할 수 없다.
  • apply, call을 사용하여 유사 배열 객체에서 배열의 메서드를 사용할 수 있음
  let obj = { 0: 'a', 1: 'b', 2: 'c', length: 3 };

  Array.prototype.push.call(obj, 'd');
  console.log(obj);	// {0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4}

  Array.prototype.psuh.apply(obj, ['e']);
  console.log(obj);	// {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', length: 5}

Array.from() 메서드
: 객체를 배열로 변환하는 ES6 문법

  let obj = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
  let arr = Array.from(obj);
  console.log(arr);	// ['a', 'b', 'c']
  1. 생성자
  • 생성자의 내부에서 다른 생성자를 호출할 때, 공통된 내용의 반복을 제거함
  function Person(name, gender) {
    this.name = name;
    this.gender = gender;
  }

  function Student(name, gender, school) {
    Person.call(this, name, gender);
    this.school = school;
  }

bind 메서드

  • call/apply 메서드와의 차이점
    : 함수를 즉시 실행하지 않고, this 값만 지정하여 새로운 함수를 선언함
  let obj = {
    a: 1,
    method: function(x, y) {
      console.log(this.a, x, y);
    }
  };

  let bindObj = obj.method.bind({a: 4});
  bindObj(5, 6);	// 4 5 6
  • 특징
  1. 부분 적용 함수를 구현할 수 있음
  let bindObj = obj.method.bind({a: 4}, 5, 6);
  bindObj(7, 8);	// 4 5 6 7 8
  1. name 속성이 추가됨
    : bind 메서드를 적용한 함수는 name 속성 사용 시, 앞에 "bound" 키워드가 붙음
  console.log(obj.method.name);	// method
  console.log(bindObj.name);	// bound method

화살표 함수

가장 가까운 scope의 객체를 this 값으로 설정하기 때문에 별도의 this binding이 필요하지 않음

profile
쥐눈(Jin Hoon)

0개의 댓글