실행 컨텍스트

yoo chang heon·2022년 4월 22일
0

JavaScript

목록 보기
8/9

실행 컨텍스트

실행 컨텍스트: 실행할 코드에 제공할 환경 정보들을 모아놓은 객체
동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성, 이를 콜스택에 쌓아 올렸다가 가장 위에 쌓여있는 컨텍스트와 관련있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장
실행환경을 구성할 수 있는 방법=> 함수를 실행하는 방법 (전역, eval)

eval() isEvil

문자로 표현된 JavaScript 코드를 실행하는 함수
전역 객체의 함수 속성.
String 생성자가 명시되어있으면 내용을 계산하지 않고 String 객체를 반환(근데 toString쓰면 계산해서줌)
직접 호출하지 않고 참조를 통해 간접저긍로 사용하면 ES5 부터 지역범위가 아니라 전역범위에서 동작=> eval()로 함수를 선언하면 전역함수가 되고, 실행되는 코드는 실행되는 위치의 지역범위에 접근할 수 없음.

문제점

절대로 사용하지 말 것
인자로 받은 코드를 caller의 권한으로 수행하는 위험한 함수. 악의적인 코드가 들어있으면 위험, 제 3자가 코드가 eval이 호출된 위치의 스코프를 볼 수 있으며, 이를 이용해 비슷한 함수인 Function 으로는 실현할 수 없는 공격이 가능


JS엔진에서 여러 코드구조를 최적화하는 것과 달리 eval은 JS 인터프리터를 사용해야 하기 때문에 느림 => JS 인터프리터는 JS를 기계 코드로 변환해서 변수명의 개념이 없어짐
window.Function이라는 대안이 있음.

실행컨텍스트는 전역 공간에서 자동으로 생성되는 전역 컨텍스트와 eval 및 함수 실행에 의한 컨텍스트 등이 있다.

실행하면 전역 컨텍스트가 콜스택에 담김=> 전역 컨텍스트와 관련된 코드들을 순차로 진행하다가 outer 함수를 호출하면 엔진이 outer 에 대한 환경 정보를 수집해서 outer 실행 컨텍스트를 생성해서 콜스택에 담음. 콜스택의 맨위에 outer니까 전역 관련된 코드 실행을 일시중지하고 outer 실행에 관련된 코드 => outer 함수 내부의 코드들을 순차로 실행

담기는 정보

  • VariableEnvironment:현재 컨텍스트 내의 식별자들에 대한 정보+ 외부 환경 정보, 선언 시점의 LexicalEnvironment의 스냅샷, 변경사항은 반영되지 않음
  • LexicalEnvironment: 변경이 실시간으로 반영, VariableEnvironment을 복사해서 만듬
  • ThisBinding: this가 바라봐야할 대상 객체

    Interpretation vs Compilation

    소스코드는 컴파일과정을 통해 사용자가 실행가능한 프로그램이 된다. => 컴파일 타임
    컴파일된 프로그램이 실행되는 과정 => 런타임

    • 컴파일 에러: 프로그램이 컴파일되는 것을 방해하는 문제 (syntax error, reference error)
    • 런타임 에러: 프로그램의 소스코드가 이미 실행가능한 프로그램으로 컴파일 된다하더라도 실행되면서 버그 발생

      Compilation

      모든 코드가 컴파일러에 의해 한꺼번에 기계어로 변환되고,컴퓨터가 실행할 수 있는 바이너리 파일로 작성
      소스코드 => 머신코드 => 프로그램 실행
      1. compilation
      2. binary file
      3. Execution
      컴파일 후 변환된 기계어를 실행시켜 실행시간이 빠르다.

      Interpretation

      코드가 인터프리터에 의해 한 줄씩 실행된다. 실행 바로 직전에 소스코드에서 기계어로 변환
      소스코드 => 프로그램 실행
      컴파일 시간이 없지만 대신 실행시간이 늘어남


      자바스크립트는 원래 pure Interpreter 언어였다. 하지만 컴파일 언어에 비해 너무 느렸고 이에 모던 자바스크립트 엔진에서 compilation과 interpretation을 같이 사용한다.
      => Just-In-Time compilation (JIT compilation)

      JIT compilation

      실제로 실행하는 시점에 바이트 코드를 기계어로 번역하는 컴파일 기법
      모든 코드가 한번에 기계어로 변환되고 바로 실행.(컴파일과 다르게 binary 파일이 생성 x, 실행도 기계어로 변환 직후에 바로 일어남)
      1. Parsing: JS 엔진이 JS코드를 파싱(의미 있는 단위로 나눠서 읽음) => AST로 만듬(문법오류검사, 기계어를 생성하기 위해 사용)
      2. Compilation: AST를 이용해 기계어를 생성
      3. Execution: 생성된 기계어를 즉시 실행
      장점) 정적 컴파일처럼 미리 바이트코드로 컴파일으 하지 않고 실행시간에 이를 결정+ 정적컴파일 처럼 최적화를 일부 적용할 수 있는 장점

      최적화 전략

      JS 엔진은 실행을 최대한 빨리 앞당기기 위해 처음에 최적화가 안된 기계어를 생성.
      실행 중에 최적화가 되고 다시 컴파일되고 실행 => 이걸 반복(메인쓰레드가 아닌 개발자가 접근 불가능한 쓰레드에서 진행)

      Adaptive JIT Compilation

      JS는 타입을 예상할 수 없음 => 이거 따지는려고 기계어 많아짐 => 최적화를 활용하는 JITC의 장점이 떨어지고, 인터프리터방식과 별다를게 없음 => 최적화시켜도 hotspot이 많이없음. => AJIT등장
      모든 코드를 일괄적으로 같은 수준의 최적화를 적용하는 것이 아니라, 바복 수행되는 정도에 따라 유동적으로 서로 다른 최적화 수준을 적용하는 것.
      기본적으로 interpreter로 수행 =>자주 반복되는 hotspot이 발견되면 그 부분에 대해서만 JITC를 적용하여 native code로 수행=> 처음에는 최소한의 최적화만 적용하는 JITC에서 더 자주 반복된다 싶은 코드들은 더 많은 최적화를 적용하는 JITC로 컴파일

      최적화 결론

      HotSpot이 얼마 없던 고전적인 JavaScript 프로그램들에는 interpreter가 JITC보다 효율이 좋다.
      최근 많이 사용되는 compute-intensive한 JS 프로그램에는 JIT가 좋다.=> 최근 엔진들은 이 둘을 모두 만족하기 위해 adaptive JITC를 채용

      꿀팁

      만약 성능이 좋은 JavaScript 코드를 만들고 싶다면, JavaScript 코드를 작성할 때 마치 C나 Java처럼 static typing 언어라고 생각하기.
      특히 array가 중요한데, 하나의 array에는 하나의 type만 넣어주는 것이 중요

      자바스크립트 런타임

      자바스크립트를 실행시키기 위해 필요한 모든 것이 포함되어있는 환경(ex: 브라우저)
      런타임에는 JS 엔진+ Web APIs+ 콜백 큐
      Web APIs: 엔진에게 필요한 기능을 제고, 윈도우 객체에 접근할 수 있도록 해줌.
      콜백 큐: 콜백 함수(이벤트 핸들러)들을 가지고 있는 곳

VariableEnvironment

LexicalEnvironment와 같지만 최초 실행 시의 스냅샷을 유지하는 것이 다름. 여기에 먼저 정보를 담은 다음 이대로 복사해서 LexicalEnvironment을 만듬. 이후 LexicalEnvironment을 주로 이용

  • environmentRecord: 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장
    컨텍스트를 구성하는 함수에 지정된 매개변수 식별자, 함수가 있을 경우 그 함수 자체, var로 선언된 변수의 식별자 등이 해당됨. 순서대로 수집 (아직 실행 전)
  • outer-EnvironmentReference
    코드가 실행되기 전이지만 코드의 변수명을 엔진이 이미 알고 있음. => 최상단으로 끌어올려놓고 실행한다고 생각해도 같음
    => 호이스팅 (실제로는 끌어올리지 않지만 끌어올린 것으로 간주)

이 둘은 LexicalEnvironment에도 존재(복사한 것이기 때문)

LexicalEnvironment

environmentRecord와 호이스팅

함수 선언문과 함수 표현식

함수선언문: function 의 정의부만 존재, 별도의 할당 명령 x

function a(){
	...
}

함수표현식: 정의한 function을 별도의 변수에 할당(기명 함수 표현식, 익명함수표현식=> 이게 일반적으로 말하는 함수표현식)

var b=function(){
	...
}
var c= function d(){ //기명 함수 표현식
	...
}

차이

함수 선언문은 전체를 호이스팅(안에 내용까지)
함수 표현식은 변수 선언부만 호이스팅

( 상대적으로 함수 표현식이 더 안전하다.)

스코프, 스코프체인, outerEnvironmentReference

스코프: 식별자에 대한 유효범위
ES5까지 JS는 전역공간을 제외하면 오직 함수에 의해서만 스코프가 생성(ES6에서는 블록에 의해서도 스코프 경계발생하게 함, but var로는 작용 안 됨. let, const, class strict mode에서의 함수선언으로 가능)
스코프 체인: 식별자의 유효범위를 안에서 바깥으로 차례로 검색해 나가는 것을 스코프 체인이라고 함. => LexicalEnvironment의 두번째 수집 자료인 outerEnvironmentReference 로 가능하게 함

outerEnvironmentReference: 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조

선언할 수 있는 시점=> 콜 스택 상에서 어떤 실행 컨텍스트가 활성화된 상태일 뿐

예시)

함수 A 내부에 함수 B 선언, 함수 B 내부에 함수 C를 선언

함수 C의 outerEnvironmentReference는 함수 B의 LexicalEnvironment를 참조, B의 LexicalEnviroment에 있는 outerEnvironmentReference는 다시 함수 B가 선언될때의 A의 Lexical을 참조 이처럼 outerEnvironmentReference는 연결리스트형태를 띰=> 계속해서 올라가면 전역 컨텍스트의 LexicalEnvironment이 있을 것.
각 outerEnvironmentReference은 오직 자신이 선언된 시점의 LexicalEnvironment만 참조하고 있으므로 가장 가까운 요소로 부터 차례로만 접근할 수 있고, 다른 순서로 접근하는 것은 불가능.
=> 변수 은닉화 가능

전역변수, 지역변수

전역변수: 전역공간에서 선언한 변수
지역변수: 함수 내부에서 선언한 변수

this

실행 컨텍스트의 thisBinding에는 this로 지정된 객체가 저장됨.
실행 컨텍스트 활성화 당시에 this가 지정되지 않은 경우 this에는 전역 객체가 저장됨.

0개의 댓글