TIL: 자바스크립트 엔진과 실행 컨텍스트..,렉시컬 스콮, prototype 체인, 클로저 - 220624

Lumpen·2022년 6월 24일
0

TIL

목록 보기
68/242

실행 컨텍스트

호출되는 시점에 해당 함수의 컨텍스트가 결정된다
변수 쉐도잉 -> 해당 컨텍스트 내에 변수가 있고 외부에 같은 이름의 변수가 있을 경우 외부 참조가 안되는? 이런 것을 하기 위해서는 호이스팅이 발생하는 편이 효율적인 것 같다......
참조는 스코프 체인에 의해 일어나는

실행 컨텍스트는 추상화된 개념을 물리적인 객체로 표현한 것..

실행 가능한 코드가 실행되기 위해 필요한 환경
자바스크립트 엔진에 의해 관리되고
자바스크립트 V8엔진은 C++로 작성되어있다
변수, thisValue, 함수 선언, 스코프와 같은 실행에 필요한 정보를
담고있다

실행 컨텍스트의 2단계

실행 컨텍스트는 다음과 같이 두 단계를 거쳐서 만들어진다.

생성 단계(Creation phase)

변수의 '선언'을 읽는다.
모든 변수는 실행 컨텍스트 생성 과정에서 참조를 위해 끌어올려(hoisted)진다 -> 호이스팅

실행 단계(Execution phase)

변수의 '값'을 읽는다. (한 줄 한 줄 실행되는 것이 이 과정이다.)

그냥 여기 보는게 이득일지도..

'컨텍스트': {
  변수객체: {
    arguments: (전역 컨텍스트인 경우 null)
    variable: [var1, func1, func2],
  },
  scopeChain: ['전역 변수객체'], ['(함수인 경우) 해당 함수 변수객체'],
  this: (전역 컨텍스트이거나 함수 컨텍스트에서 따로 설정하지 않은 경우 window),

출처

코드를 실행하면 실행 컨텍스트 스택(Stack)이 생성하고 소멸한다. 현재 실행 중인 컨텍스트에서 이 컨텍스트와 관련없는 코드(예를 들어 다른 함수)가 실행되면 새로운 컨텍스트가 생성된다. 이 컨텍스트는 스택에 쌓이게 되고 컨트롤(제어권)이 위로(위에 쌓인 컨텍스트로) 이동한다.

  1. 컨트롤이 실행 가능한 코드로 이동하면 논리적 스택 구조를 가지는 새로운 실행 컨텍스트 스택이 생성된다. 스택은 LIFO(Last In First Out, 후입 선출)의 구조를 가지는 나열 구조이다.

  2. 전역 코드(Global code)로 컨트롤이 진입하면 전역 실행 컨텍스트가 생성되고 실행 컨텍스트 스택에 쌓인다. 전역 실행 컨텍스트는 애플리케이션이 종료될 때(웹 페이지에서 나가거나 브라우저를 닫을 때)까지 유지된다.

  3. 함수를 호출하면 해당 함수의 실행 컨텍스트가 생성되며 직전에 실행된 코드 블록의 실행 컨텍스트 위에 쌓인다.

  4. 함수 실행이 끝나면 해당 함수의 실행 컨텍스트를 파기하고 직전의 실행 컨텍스트에 컨트롤을 반환한다.

전역 컨텍스트와 함수 컨텍스트

전역 컨텍스트

코드 실행시 생성되는 모든 것을 포함하여 관리하는 컨텍스트로 페이지가 종료될 때까지 유지된다.

함수 컨텍스트

함수를 호출할 때마다 생성되는 컨텍스트로 함수 실행이 마무리되면 해당 컨텍스트는 사라진다. (클로저의 경우는 제외)

실행 컨텍스트의 3가지 객체

변수 객체, 스코프 체인, this

변수 객체

  1. 변수
  2. 매개변수(parameter)와 인수 정보(arguments)
  3. 함수 선언(함수 표현식은 제외)

스코프 체인

스코프 체인(Scope Chain)은 일종의 리스트로서 전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례로 저장하고 있다. 다시 말해, 스코프 체인은 해당 전역 또는 함수가 참조할 수 있는 변수, 함수 선언 등의 정보를 담고 있는 전역 객체(GO) 또는 활성 객체(AO)의 리스트를 가리킨다.

현재 실행 컨텍스트의 활성 객체(AO)를 선두로 하여 순차적으로 상위 컨텍스트의 활성 객체(AO)를 가리키며 마지막 리스트는 전역 객체(GO)를 가리킨다.
스코프 체인은 식별자 중에서 객체(전역 객체 제외)의 프로퍼티가 아닌 식별자, 즉 변수를 검색하는 메커니즘이다.
식별자 중에서 변수가 아닌 객체의 프로퍼티(물론 메소드도 포함된다)를 검색하는 메커니즘은 프로토타입 체인(Prototype Chain)이다.

함수 내의 코드에서 변수를 참조하면 엔진은 스코프 체인의 첫번째 리스트가 가리키는 AO에 접근하여 변수를 검색한다. 만일 검색에 실패하면 다음 리스트가 가리키는 Activation Object(또는 전역 객체)를 검색한다. 이와 같이 순차적으로 스코프 체인에서 변수를 검색하는데 결국 검색에 실패하면 정의되지 않은 변수에 접근하는 것으로 판단하여 Reference 에러를 발생시킨다

스코프 체인은 함수의 감추인 프로퍼티인 [[Scope]]로 참조할 수 있다.

엔진은 스코프 체인을 통해 렉시컬 스코프를 파악한다. 함수가 중첩 상태일 때 하위함수 내에서 상위함수의 스코프와 전역 스코프까지 참조할 수 있는데 이것는 스코프 체인을 검색을 통해 가능하다.

this value

this 프로퍼티에는 this 값이 할당된다. this에 할당되는 값은 함수 호출 패턴에 의해 결정된다.

변수 선언 처리가 끝나면 다음은 this value가 결정된다. this value가 결정되기 이전에 this는 전역 객체를 가리키고 있다가 함수 호출 패턴에 의해 this에 할당되는 값이 결정된다. 전역 코드의 경우, this는 전역 객체를 가리킨다.

Variable Instantiation(변수 객체화) 실행

  1. (Function Code인 경우) 매개변수(parameter)가 Variable Object의 프로퍼티로, 인수(argument)가 값으로 설정된다.
  2. 대상 코드 내의 함수 선언(함수 표현식 제외)을 대상으로 함수명이 Variable Object의 프로퍼티로, 생성된 함수 객체가 값으로 설정된다.(함수 호이스팅)
  3. 대상 코드 내의 변수 선언을 대상으로 변수명이 Variable Object의 프로퍼티로, undefined가 값으로 설정된다.(변수 호이스팅)

렉시컬 스코프

lexical scope (Static scope)

선언 당시 환경에 대한 정보를 담은 객체

함수의 호출이 아닌 선언 시점에 해당 변수의 상위 스코프가 결정된다. (스코프 체인이 결정된다.)

실행 컨텍스트와 lexical scope는 다른 개념이다. 실행 컨텍스트는 함수 호출시마다 생성되지만, scope chain은 lexical scoping에 따라 함수 선언시에 생성된다. 코드의 문맥은 lexical scope로 이루어지며 이를 구현한 것이 실행 컨텍스트이다. 모든 코드는 실행 컨텍스트에서 평가되고 실행된다.

prototype

프로토타입은 자바스크립트 언어 레벨에서 모든 객체가 가지고 있는 부모 객체

https://poiemaweb.com/js-prototype
모든 객체는자신의 부모 역할을 담당하는 객체와 연결되어 있다. 그리고 이것은 마치 객체 지향의 상속과 같이 부모 객체의 프로퍼티 또는 메소드를 상속받아 사용할 수 있게 한다. 이러한 부모 객체를 Prototype(프로토타입) 객체 또는 줄여서 Prototype(프로토타입)이라 한다

자바스크립트의 모든 객체는 [[Prototype]]이라는 인터널 슬롯(internal slot)를 가진다. [[Prototype]]의 값은 null 또는 객체이며 상속을 구현하는데 사용된다. [[Prototype]] 객체의 데이터 프로퍼티는 get 액세스를 위해 상속되어 자식 객체의 프로퍼티처럼 사용할 수 있다. 하지만 set 액세스는 허용되지 않는다.
-> 가장 상위 prototype객체인 Object.prototype의 [[Prototype]]의 값은 null이지 않을까.. Object.prototype은 프로토타입이 없는 아주 드문 객체로 아무런 프로퍼티도 상속받지 않는다

[[Prototype]]의 값은 Prototype(프로토타입) 객체이며 proto accessor property로 접근할 수 있다. proto 프로퍼티에 접근하면 내부적으로 Object.getPrototypeOf가 호출되어 프로토타입 객체를 반환한다.

객체는 proto 프로퍼티로 자신의 부모 객체(프로토타입 객체)인 Object.prototype을 가리키고 있다.

함수 객체는 일반 객체와는 달리 prototype 프로퍼티도 소유하게 된다.

Prototype chain

자바스크립트는 특정 객체의 프로퍼티나 메소드에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티 또는 메소드가 없다면 [[Prototype]]이 가리키는 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 차례대로 검색한다. 이것을 프로토타입 체인이라 한다.

프로토타입 체인 동작 조건

객체의 프로퍼티를 참조하는 경우, 해당 객체에 해당 프로퍼티가 없다면, 프로토타입 체인이 동작한다.

생성자 함수로 생성된 객체

Object() 생성자 함수는 물론 함수이다. 따라서 함수 객체인 Object() 생성자 함수는 일반 객체와 달리 prototype 프로퍼티가 있다.

클로저

클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.

스코프는 함수를 호출할 때가 아니라 함수를 어디에 선언하였는지에 따라 결정된다. 이를 렉시컬 스코핑(Lexical scoping)라 한다.
-> this는 호출된 곳에 따라 달라짐

함수 innerFunc가 함수 outerFunc의 내부에 선언된 내부함수이므로 함수 innerFunc는 자신이 속한 렉시컬 스코프(전역, 함수 outerFunc, 자신의 스코프)를 참조할 수 있다. 이것을 실행 컨텍스트의 관점에서 설명해보자.

실행 컨텍스트의 관점에 설명하면, 내부함수가 유효한 상태에서 외부함수가 종료하여 외부함수의 실행 컨텍스트가 반환되어도, 외부함수 실행 컨텍스트 내의 활성 객체(Activation object)(변수, 함수 선언 등의 정보를 가지고 있다)는 내부함수에 의해 참조되는 한 유효하여 내부함수가 스코프 체인을 통해 참조할 수 있는 것을 의미한다.
-> 가비지 컬렉터는 참조되고 있는 것에 대해 메모리 회수를 하지 않기 때문에
-> 클로저를 사용한다면 메모리 누수에 대해 고민해봐야 함

내부함수 innerFunc가 호출되면 자신의 실행 컨텍스트가 실행 컨텍스트 스택에 쌓이고 변수 객체(Variable Object)와 스코프 체인(Scope chain) 그리고 this에 바인딩할 객체가 결정된다. 이때 스코프 체인은 전역 스코프를 가리키는 전역 객체와 함수 outerFunc의 스코프를 가리키는 함수 outerFunc의 활성 객체(Activation object) 그리고 함수 자신의 스코프를 가리키는 활성 객체를 순차적으로 바인딩한다. 스코프 체인이 바인딩한 객체가 바로 렉시컬 스코프의 실체이다.

내부함수 innerFunc가 자신을 포함하고 있는 외부함수 outerFunc의 변수 x에 접근할 수 있는 것, 다시 말해 상위 스코프에 접근할 수 있는 것은 렉시컬 스코프의 레퍼런스를 차례대로 저장하고 있는 실행 컨텍스트의 스코프 체인을 자바스크립트 엔진이 검색하였기에 가능한 것이다.

profile
떠돌이 생활을 하는. 실업자는 아니지만, 부랑 생활을 하는

0개의 댓글