실행 컨텍스트(Execution Context)

Deprecated Velog·2024년 1월 27일

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
Host is renewing Velog to Tistory. New link will be soon...

0개의 댓글