JS

박지원·2021년 9월 2일
0

실행 컨텍스트

실행컨텍스트는 scope, hoisting, this, function , closer 등의 동작원리를 담고있는 핵심원리입니다.

실행 컨텍스트는 실행가능한 코드를 형상화하고 구분하는 추상적인 개념 을 의미합니다. 쉽게말해 실행 가능한 코드가 실행되기 위해 필요한 환경

Javascript 해석기는 함수 혹은 스크립트를 실행하려고 할 때 새로운 Context를 만들어 냅니다.

자바스크립트 엔진은 코드를 실행하기 위해

  • Variable Object(VO / 변수객체)
    • 변수(전역변수, 지역변수, 매개변수, 객체의 프로퍼티), 함수선언
  • Scope Chain
    • 변수의 유효범위 (scope)
  • thisValue
    • context object(this)

등에 대한 정보를 알고 있어야합니다. 이것들처럼 실행에 필요한 정보를 형상화하고 구분하기 위해 자바스크립트 엔진은 실행 컨텍스트를 물리적 객체형태로 관리합니다.

var x = 'xxx';

function foo () {
  var y = 'yyy';

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

이 코드가 실행되면 실행컨텍스트 스택이 생성되고 소멸됩니다.

Untitled

컨트롤(제어권)이 실행가능한 코드로 이동하면 논리적 스택구조를 가지는 새로운 실행컨텍스트 스택이 생성되고 전역코드로 컨트롤이 진입하면 전역실행컨텍스트(GEC)가 실행컨텍스트 스택에 쌓이고 애플리케이션이 종료될 때 까지 유지됩니다.

함수가 호출되면 함수 실행컨텍스트가 생성되고 직전에 실행된 코드블록의 실행컨텍스트위에 쌓이고 끝날때 해당 함수의 실행컨텍스트는 파기하고 직전의 실행컨텍스트에 컨트롤을 반환합니다.

이후 내용은 여기를 참조

여기까지 설명한 내용은 사실 ES5 이전의 개념입니다. ES5부터 실행컨텍스트는 물리적으로 다른 모양을 갖습니다.

변수객체(VO), 활성화 객체, 스코프 체인등의 개념이 LexicalEnvironment로 변경되었습니다.

ExcutionContext = {
    LexicalEnvironment = [Lexical Environment],
    VariableEnvironment = [Lexical Environment],
    thisbinding = [Object]
}

Untitled

다시한번 실행 컨텍스트의 실행 흐름

자바스크립트 엔진은 script tag를 만나면 먼저 전역 실행컨텍스트를 만들어서 이를 실행컨텍스트 스택에 push하고 함수가 호출, 종료될 때 마다 새로운 실행 컨텍스트를 만들어서 LIFO 방식으로 push pop합니다

엔진이 실행컨텍스트를 생성하는 과정

Creation Phase (생성 단계)

  • LexicalEnvironmnet Component를 만듭니다
  • VariableEnvironment Component를 만듭니다.
  • 현재 EC의 this 값을 결정 합니다.

LexicalEnvironment

LexicalEnvironment = {
    "Environment Record" = ,
    "Outer Environment Reference" = ,
}

식별자(identifier)와 특정 변수(variable)의 맵핑을 지속적으로 트래킹하는 엔진의 내부 구조입니다. LE(Lexical Environment)는 함수, 블록문, catch절 이 평가될 때 즉 {}블록문이 있을 때 생성됩니다. (호출 될 때가 아니라 선언될 때)

렉시컬 환경은 함수 선언 및 변수(let, const)바인딩을 저장하는데 사용

Lexical Environment를 scope라고 부르는 경우가 많음

Lexical Environment는 JS 코드에서 식별자 정의를 위해 사용하는 객체로 Environment RecordOuter Environment Reference 를 프로퍼티로 갖습니다.

  1. Environment Record (ER)

    • 유효범위 내의 값에 식별자(identifier)를 매핑함

    • DeclarativeER : 변수 선언 및 함수 선언 저장

      → 변수 선언, 함수 선언, catch절 에서 사용되는 식별자:값 맵핑 정보를 담음 ( 실제 값 저장 )

    • ObjectER : with문이나 전역 환경에서 사용됨.

      → with문과 같이 식별자를 어떤 특정 객체의 A의 속성으로 취급할 때 사용, bindingObject라는 프로퍼티로 A를 가리키는 참조가 저장됨

    함수의 경우 arguments 객체 포함

  2. Outer Environment Reference (Scope chain)

    • 중첩 유효범위를 가질 수 있는 환경에서 상위 LE를 참조
    • 즉 외부환경에 대한 참조
    • 전역 환경에서는 null

Scope Chain은 ES5부터 Lexical nesting structure 등으로 표현함.

Variable Environment

Lexical Environment와 대게 동일한 값을 갖지만 만들어진 변수 선언 및 함수 선언에 대해 바인딩을 유지함.

변수(var)바인딩만 저장

LexicalEnvironment는 코드 실행중에 실행 컨텍스트 내에서 변경될 수 있지만 VariableEnvrionment는 항상 값을 유지

  • LexicalEnvironment는 일시적으로 LexicalEnvironment 하위의 새로운 환경을 가리키고
  • 이 새로운 환경은 임시 바인딩을 보유합니다.
  • 그리고 임시 범위를 벗어나면 VariableEnvrionment가 참조하고 있는 값으로 LexicalEnvironment를 복구합니다.

Execution phase(실행 단계)

이전 단계인 생성단계에서는 변수의 선언이 이뤄지고 전역 실행 컨텍스트의 this가 결정됨

그리고 실행 단계에서 변수에 값이 할당되고 코드가 실행됨.

this

자바스크립트에서 모든 함수는 실행될 때마다 함수 내부에(lexical environment) this라는 객체가 추가됩니다. arguments라는 유사 배열 객체와 함께 함수 내부로 암묵적으로 전달 되는 것이기 때문에 this는 함수가 호출 된 상황에 따라 그 모습을 달리합니다.

  1. 함수를 호출 했을 때 this는 전역객체를 의미합니다.
  2. 객체의 소속인 메소드의 this는 그 객체를 가리킨다.
  3. 생성자의 호출에서는 this값이 생성될 객체를 가리킨다.
  4. eventListener 에서 this는 e.currentTarget 을 의미한다.
  5. arrow function 에서는 상위의 this값을 그대로 사용

암묵적 바인딩 외에도(apply/call/bind 메서드로) 명시적으로 바인딩이 가능

  1. apply는 인수배열을 받고 , call은 인수 목록을 받는다. 둘다 일단은 함수를 호출함.
  2. bind는 함수를 호출하지 않고 함수를 변수에 담아두고 있다가 나중에 사용할 수 있습니다.

let var const 차이

요약

  • var - function-level scope - 재선언 O, 재할당 O
  • let - block-level scope - 재선언 X, 재할당 O
  • const - block-level scope - 재선언 X, 재할당 X

우선 var는 function-level scope이고 변수 선언 방식에 있어서 큰 단점을 가지고 있습니다. 변수를 한번 더 선언해도 각기 다른 값을 출력하게 되구요 이때문에 let, const가 등장했습니다.

let과 const는 재선언이 불가능하고 block level scope를 가집니다. 하지만 let은 mutable하고 const는 immutable한 차이를 가집니다.

var는 호이스팅이 일어날때 변수를 메모리 공간에 할당하고 undefined로 초기화 하지만 let const은 변수가 선언된 장소에 인터프리터가 도달할 때 값이 초기화됩니다.

하지만 const도 object가 담기면 object의 수정은 막을 수 없습니다. const에 할당된 object의 주소값은 불변이겠지만, object의 값이나 상태는 여전히 변경가능합니다. 따라서 freeze 를 사용하여 object를 수정못하게 막을 수 있습니다

Call by value(값에 의한 호출)

  • 장점 : 값을 복사하여 처리하기 때문에 안전합니다. 원래의 값이 보존이 된다.
  • 단점 : 값을 복사를 하기 때문에 메모리 사용량이 늘어난다.

Call by reference(참조에 의한 호출)

  • 장점 : 값을 복사하지 않고 직접 참조를 해서 빠릅니다.
  • 단점 : 직접 참조를 하기때문에 원래 값이 영향을 받는다.

호이스팅

호이스팅은 자바스크립트 함수가 실행되기 전에 함수안에 있는 필요한 변수값을 모두 모아서 유효범위(Lexical Environment)의 최상단에 선언해주는 것을 의미합니다.

var과 함수선언문 에서는 호이스팅이 일어나면 var은 undefined로 초기화되고 함수선언문은 함수가 통째로 호이스팅되면서 에러를 일으키지 않습니다. 하지만 함수 표현식에서는 정상적으로 함수가 실행되지 않을 수 있습니다. 호이스팅이 일어나도 해당 변수가 함수로 인식되지 않기 때문입니다.

//ex
foo(); // Error
var foo = function(){
	console.log('hi');
}

TDZ(Temporal Dead Zone)

스코프의 시작지점부터 초기화 시작지점까지의 구간을 TDZ라고 합니다. TDZ 시맨틱은 선언 전에 변수에 접근하는 것을 금지합니다.

호이스팅은 var과 함수선언문에서만 일어나는 것은 아닙니다.

하지만 const 변수, let 변수, class 구문, constructor() 내부의 super(), 기본 함수 매개변수 는 TDZ의 영향을 받습니다.

javascript에서 변수는 선언, 초기화, 할당이라는 3단계에 걸쳐서 생성이됩니다.

  1. 선언 : 변수를 실행 컨텍스트의 변수 객체에 등록하는 단계. 변수 객체는 스코프가 참조하는 대상이됩니다.
  2. 초기화단계 : 실행 컨텍스트에 존재하는 변수 객체에 선언 단계의 변수를 위한 메모리를 만들고. 이 단계에서 할당된 메모리에는 undefined로 초기화됩니다.
  3. 할당 단계 : 사용자가 undefined로 초기화된 메모리에 다른 값을 할당하는 단계.
  • var 변수는 선언과 초기화를 동시에 진행하여 실행컨텍스트 변수 객체에 변수를 등록하고 메모리를 undefined로 만들어 버립니다.
  • let, const 에서도 호이스팅은 발생하지만 선언과 초기화 단계가 분리되어 진행되기 때문에 실행컨텍스트에 변수를 등록했지만. TDZ에 의해 uninitialized 상태가 됩니다. javascript 엔진은 uninitialized 상태의 변수를 인지하긴 하지만 렉시컬 바인딩이 실행되기 전까지 엑세스할 수 없습니다.
  • function 은 선언, 초기화, 할당 단계를 동시에 진행.

클로저

클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment or 스코프)을 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말한다. 이를 조금 더 간단히 말하면 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수다라고 말할 수 있겠다.

일반적으로는 스코프에서 빠져나가면 해당 스코프에서 선언한 변수는 메모리에서 제거해도 안전합니다. 그럼에도 함수를 정의해 클로저를 만들면 접근할 수 없던 것들에 접근할 방법이 생기는 부수적인 특징이 있습니다.

좀 더 정확히 말하면 클로저는

내부함수의 Lexical Environment의 Outer Lexical Environment Reference(스코프체인)가 참조하고있는 외부함수의 LE를 저장해서 자신을 포함하는 외부함수의 EC가 사라지더라도 OLER을 통해(스코프체인)을 통해 참조 가능.

자신을 포함하는 외부함수의 렉시컬환경이 소멸돼도 [[Scopes]] 프로퍼티가 가리키는 외부함수의 렉시컬환경은 소멸되지 않음 이것이 클로저

장점

  1. 데이터 보존

    클로저 함수는 외부 함수 실행이 끝나도 외부 함수 내의 변수 사용이 가능함. 특정 데이터를 스코프 안에 가둬서 사용할 수 있게하는 폐쇄성

  2. 정보 접근 제한(캡슐화)

    모듈 패턴을 사용해 객체에 담아 여러개의 함수를 리턴하게 만듬.

  3. 모듈화에 유리

    클로저 함수를 각각의 변수에 할당하면 각자 독립적으로 값을 사용하고 보존할 수 있음. 이를 통해 데이터와 메소드를 묶어다닐 수 있음.

단점

폐쇄된 스코프 안의 변수가 스코프 종료와 동시에 회수되지 않음. 스코프 밖에서 언제든지 변수를 호출할 가능성이 있기 때문에 가비지컬렉터에 의해 수거되지 않음. 메모리 누수나 누적에 대한 고민이 필요함.

→ 메모리 해제하면 됨

가비지 컬렉터란?

프로토타입이란?

프로미스란

렌더링최적화, SEO, 코딩컨벤션, AJAX, XMLHTTPREQUEST FETCH axios, async await

javascript 성능개선, 비동기통신, 렌더링최적화,JWT

https://reese-dev.netlify.app/javascript/execution-context/

https://www.inflearn.com/questions/64181

https://iamsjy17.github.io/javascript/2019/06/10/js33_execution_context.html

0개의 댓글