[JS] 실행 컨텍스트

PARK·2021년 4월 8일
0

javaScript

목록 보기
2/4
post-thumbnail

실행 컨텍스트

실행 컨텍스트의 자바스크립트가 코드를 실행하기 위해서 알아야할 변수, 함수선언, scope, this를 객체 형태로 나타낸 것이 실행 컨텍스트 입니다. 실행 컨텍스트는 호출 스택을 생성 원리이고, 동기식 처리의 원리이기도 합니다.

실행 컨텍스트는 자바스크립트가 동작하는 기본 원리이기도해서 lexical scoping, hoisting, closure 등과 밀접하게 연관되어 있습니다.

컨텍스트 종류

실행 컨텍스트는 3가지로 나누어집니다.
전역 컨텍스트 (Global Context)
함수 컨텍스트 (Function Context)
eval 함수 컨텍스트 (Function Context)

우리는 이 중에서 전역컨텍스트와 함수 컨텍스트를 살펴보겠습니다. (eval로 실행되는 코드는 별로 권장하지 않기 때문입니다.)

전역 컨텍스트는 자바스크립트가 실행되는 순간 생성됩니다. 이 컨텍스트는 페이지가 종료될 때까지 유지됩니다.
함수 컨텍스트는 선언이 아니라 호출될 때 생성됩니다. 렉시컬 스코핑과 잘 구분해야 합니다.

3가지 객체

실행 컨텍스트가 생성되면 안에 3가지 객체가 같이 만들어집니다. 변수객체(활성 객체), Scope Chain, this입니다.

변수 객체는 말 그대로 변수를 담습니다. 변수와 함수 선언(표현식은 x)등의 정보를 담습니다. 활성 객체는 함수 컨텍스트에서 생성되는 변수 객체입니다. 특징은 argument를 추가로 갖습니다.

변수객체 변수 함수선언
활성객체 변수객체 + 매개변수

앞으로는 편하게 변수객체로 부르겠습니다.

Scope chain(스코프 체인)은 자기 자신의 스코프와 상위 스코프들의 변수 객체입니다.

this는 new를 통해서 객체를 따로 생성하지 않았다면 원래 기본인 window입니다.

생성원칙

먼저 전역 컨텍스트 하나 생성 후, 함수 호출 시마다 컨텍스트가 생깁니다.
컨텍스트 생성 시 컨텍스트 안에 변수객체(arguments, variable), scope chain, this가 생성됩니다.
컨텍스트 생성 후 함수가 실행되는데, 사용되는 변수들은 변수 객체 안에서 값을 찾고, 없다면 스코프 체인을 따라 올라가며 찾습니다.함수 실행이 마무리되면 해당 컨텍스트는 사라집니다.(클로저 제외) 페이지가 종료되면 전역 컨텍스트가 사라집니다.

var x = 'xxx';

function foo () {
  var y = 'yyy';
    console.log(x);
  function bar () {
    var z = 'zzz';
    console.log(x + y + z);
  }
  bar();
}

foo();
---------------------------------
'전역 컨텍스트': {
    변수객체: {
    arguments: null,
    variable: ['x', 'foo'],
    },
    scopeChain: ['전역 변수객체'],
    this: window,
}

전역 컨텍스트는 당연히 변수 객체임으로 인자가 없습니다. 변수는 foo와 x가 있습니다. 스코프체인은 자기 자신이고 this도 역시 window입니다.

컨텍스트가 생성되고 호이스팅이 발생합니다. 함수 선언식은 컨텍스트 생성 후 바로 식 자체가 최상단으로 끌어올려집니다. 그래서 선언과 동시에 초기화가 됩니다(문제없이 실행됨). var은 최상단으로 올라가지만 초기화되지는 않습니다(undefiend).

'전역 컨텍스트': {
    변수객체: {
    arguments: null,
    variable: ['x', {foo: Function}], //foo가 함수라는 것을 알고 있음
    },
    scopeChain: ['전역 변수객체'],
    this: window,
}

변수(함수 표현식 포함)는 코드가 순차적으로 실행되면서 초기화 됩니다.

'전역 컨텍스트': {
    변수객체: {
    arguments: null,
    variable: [{x: xxx}, {foo: Function}],
    },
    scopeChain: ['전역 변수객체'],
    this: window,
}

그리고 foo()를 호출하면서 함수 컨텍스트가 생성됩니다.

'foo 컨텍스트': {
    변수객체: {
    arguments: null,
    variable: ['y', 'bar'], //생성 후 순차적으로 실행되면서 초기화
    },
    scopeChain: ['foo 변수객체', '전역 변수객체'],
    this: window,
}

scopeChain은 내부함수가 외부함수로 스코프를 찾아 올라가는 관계를 말합니다. 그 말은 foo스코프에서 변수를 찾고, 없으면 전역 스코프로 찾아 올라갑니다. 중요한 점은 렉시컬 스코프를 따른다는 점입니다. 함수 호출 시점이 중요한 호출 스택과는 다릅니다.

console.log(x);

x는 foo 변수 객체에 없으므로 스코프 체인을 따라서 전역으로 갑니다. x는 xxx로 출력됩니다.

bar을 호출하면서 호출하면서 함수 컨텍스트가 생성됩니다.

'bar 컨텍스트': {
    변수객체: {
    arguments: null,
    variable: ['z'], //생성 후 순차적으로 실행되면서 초기화
    },
    scopeChain: ['전역 변수객체', 'foo 변수객체', 'bar변수객체'],
    this: window,
}

y도 똑같이 스코프 체인을 따라서 foo로 올라가 찾아냅니다.

다른 예제 입니다.

var a = 1;
function outer() {
  function inner() {
    console.log('inner:', a); // 1. ?
    var a = 2;
  }
  inner();
  console.log('outer:', a); // 2. ?
}
outer();
console.log('global:', a); // 3. ?

inner 컨텍스트의 스코프체인입니다.

scopeChain: ['inner 변수객체', 'outer변수 객체', '전역 변수객체'],

스코프 체인은 위와 같은 순서임으로 inner을 먼저 찾습니다. var a는 호이스팅으로 최상단으로 올라가기 때문에 1번 a는 undefined를 출력시킵니다. 2번과 3번은 체인을 따라서 1을 출력시킵니다.

스코프 체인 마지막 예제를 보겠습니다.

var x = 1;

function foo() {
  var x = 10;
  bar();
}

function bar() {
  console.log(x);
}

foo(); // ?
bar(); // ?

---------------------------------

  'bar 컨텍스트': {
    변수객체: {
      arguments: null,
      variable: null,
    },
    scopeChain: ['bar변수객체', '전역 변수객체'],
    this: window,
  }

bar 컨텍스트는 분명히 foo안에서 호출되기때문에 스코프 체인이

scopeChain: ['bar변수객체', 'foo변수객체', '전역 변수객체']

로 생성된다고 오해할 수 있습니다. 하지만 스코프 체인은 렉시컬 스코프를 따라서 구축된다는 점을 짚고 넘어가야합니다. 호출과는 상관없습니다. foo와 bar 모두 1을 출력합니다.

클로저

질문

클로저와 실행컨텍스트 관계가 궁금하다. 서칭을 해봐도 자료가 많지 않았다. 클로저는 내부함수가 유효한 상태에서 외부함수가 종료하여 외부함수의 실행 컨텍스트가 반환되어도, 안에 유효했던 내부함수가 스코프 체인을 통해 남아 있는 것을 말한다. 반복문과 비동기 함수가 만날 때 클로저가 자주 발생한다. 클로저는 다음과 같은 상황에서 사용한다.

  • 1 무분별한 전역 변수를 억제한다
    2 상태유지
    3 캡슐화

참고: https://www.zerocho.com/category/JavaScript/post/5741d96d094da4986bc950a0

https://poiemaweb.com/js-scope#7-%EB%A0%89%EC%8B%9C%EC%BB%AC-%EC%8A%A4%EC%BD%94%ED%94%84

https://velog.io/@wogkr1383/%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8Execution-Context

https://estaid.dev/relationship-between-execution-context-and-hoisting/

profile
익숙한 것에 작별을 고해야한다

0개의 댓글