1. ES3/ES5 스펙의 아키텍처, 메커니즘 관련 키워드
ES3/ES5 스펙의 아키텍처(개요)
ES3 Spec
•
Execution Context : 실행 콘텍스트로 함수가 호출되었을 때 함수가 실행될 수 있는 환경과 결과를 저장하는 영역. 즉, 함수의 모든 처리는 Execution Context안에서 이뤄집니다.
◦
Definition
1.
Function Oject:
⇒엔진이 function 키워드를 만났을때 만드는 Function 키워드
2.
Types of Executable Code
⇒실행 가능 코드로 Global, Eval Function code가 있습니다.
3.
Variable Instantiation
⇒변수의 인스턴스화 변수를 어떻게 인스턴스화해서 처리할 것인지에 대한 개념
4.
Scope Chain and Identifier Resolution
⇒ 스코프체인과 식별자 해결(결정) 즉, 함수를 호출할 때 어떻게 함수 이름을 찾을 것인가와 변수의 이름을 설정할 때 어떻게 변수를 찾을것인가에 대한 개념
5.
Global Object
⇒전역 객체로 Function Object와 다 동일하지만 영역이 다르기 때문에 구분한다.
6.
Activation Object
⇒ 함수를 실행할 수 있는 환경과 함수가 실행되었을 때 결과를 저장하는 오브젝트
7.
This
⇒인스턴스에서 중요한 역할
8.
Arguments Object
⇒ 함수의 파라미터를 처리하는 오브젝트
◦
Entering An Execution Context
1.
Global Code
2.
Eval Code
3.
Function Code
ES5 Spec
•
Executable Code and Execution Contexts
⇒실행 컨텍스트는 ES3와 동일
1.
Types of Executable Code
a.
Strict Mode Code
⇒ 'use strict' 로 작성했을 때의 실행 모드
2.
Lexical Environments
⇒ JS ES5에서 정적인 환경을 취합니다. 실행 컨텍스트 안에서 환경적인 측면을 처리하는 것이 Lexical Environments입니다.
a.
Environment Records
⇒ 함수가 호출되어 실행될때와 그 전에 그 상황들을 기록하는 것
b.
Lexical Environment Operations
⇒ 어떤 변수에 값을 할당했을 때 정적인 환경에 설정하는 것
c.
The Global Environment
⇒ Global Object를 처리하는 환경입니다.
3.
Execution Contexts
a.
Identifier Resolution
⇒ 식별자 해결은 함수를 호출했을 때 어디서 찾을 것인가에 대한 개념
4.
Establishing an Execution Context
a.
Entering Global Code
⇒ 함수 안으로 엔진 컨트롤이 이동했을때 코드를 어떻게 처리할 것인가.
b.
Entering Eval Code
⇒ 함수 안으로 엔진 컨트롤이 이동했을때 코드를 어떻게 처리할 것인가.
c.
Entering Function Code
⇒ 함수 안으로 엔진 컨트롤이 이동했을때 코드를 어떻게 처리할 것인가.
5.
Declaration Binding Instantiation
⇒ 변수를 어떻게 실행 컨텍스트의 Environment에 바인딩 시킬 것인가.
6.
Arguments Object
⇒ 함수의 파라미터를 처리하는 오브젝트
2. 엔진 관점의 핵심 키워드
ES3 Spec
•
Execution Contexts
1.
Definition
a.
Function Objects
b.
Types of Executable Code
c.
Variable Instantiation
d.
Scope Chain and Identifier Resolution
e.
Global Object
f.
Activation Object
g.
This
h.
Arguments Object
2.
Entering An Execution Context
a.
Global Code
b.
Eval Code
c.
Function Code
ES5 Spec
•
Excutable Code and Execution Contexts
1.
Types of Executable Code
a.
Strict Mode Code
2.
Lexical Environments
a.
Environment Records
b.
Lexical Environment Operations
c.
The Global Environment
3.
Execution Contexts
a.
Identifier Resolution
4.
Establishing an Execution Context
a.
Entering Global Code
b.
Entering Eval Code
c.
Entering Function Code
5.
Declaration Binding Instantiation
6.
Arguments Object
ES3와 ES5의 가장 큰 차이점
ES3에서는 Scope Chain, ES5에서는 Lexical Environments개념이 가장 큰 차이점
정리
실행 컨텍스트(Execution Context)와 식별자 해결(Identifier Resolution)
엔진 처리는 크게 해석과 실행으로 나눌 수 있습니다.
해석이란 컴파일과 실행할 환경을 설정하는 것이고(Context), 실행이란 해석단계에서 설정된 환경을 바탕으로 코드를 실행하는 것입니다.(Execution) 그렇기에 실행 환경을 만드는 것은 함수가 호출되기 전/후로 가능하지만, 실행은 함수가 호출 된 다음에 가능합니다.(Identifier Resolution) 즉, 함수라는 단위를 어떻게 묶어서 취급할 것이냐라는 것입니다.
예를 들어, 여러박스에 함수에 있는 내용을 분산시키면 실행될수도 없고 올바르지 않습니다. 반면, 하나의 박스(Context)안에 함수에서 발생할 수 있는 것을 모두 넣을 수 있으면 메모리에도 하나의 박스만 올리면되기에 간단해집니다.
함수가 호출되었을 때 어떻게 실행하는 모음을 가져갈 것인지에 대한것이 Execution Context입니다. 그런데 어째서 Execution Context를 묶음으로 가져가야할까요? 그이유는 식별자 해결(Identifier Resolution)에 있습니다.
함수가 호출되었을 때 함수의 이름을 찾아야하는데 그게 Execution Context안에 있다면 찾기가 쉬워집니다.
하지만, Execution Context밖에 존재하게 된다면 엔진처리도 복잡해지고 비용도 높아지게 됩니다. 그렇기에 Execution Context를 묶음으로 가져가는 것입니다.
그리고 이 식별자 해결(Identifier Resolution)에서 파생된 키워드가 Scope입니다. 식별자가 어디에 있는지에 대해서 구조적으로 가져가는데 이것도 엔진 관점에서 볼 때 비용이 부담되기 때문입니다.
엔진의 기본적인 방향은 분산되지 않고 컨텍스트 개념으로 하나의 묶음 안에서 동작하겠다는 것입니다.
결국, 식별자 해결을 하기위해 컨텍스트가 필요하고 함수가 호출되었을 때 식별자 해결을 하게되면서 실행 컨텍스트로 묶어버리는 것입니다. 그리고 이걸 어떤 단위로 묶을 것인가에 대해서 ES3에서는 Scope Chain 개념을 사용했고, ES5에서는 Lexical Environments라는 개념을 사용했습니다.
Scope Chain 과 Lexical Environments 개념은 컨텍스트가 다릅니다. 컨텍스트는 하나의 묶음으로 가져가는게 최선인데, 여러개로 분산되어있고 각각의 요소(Scope)마다 찾아가서 자원을 가져오는 것은 비용소모가 큽니다.
Lexical Environment
ECMAScript 코드의 렉시컬 중첩 구조를 기반으로 하는 함수와 특정 변수들에 대한 식별자를 정의하는 specification 타입.
Lexical Environment는Environment Record와 null이 허용되는 외부 Lexical Environment로 구성됩니다.
일반적으로 Lexical Environment는 FunctionDeclaration, WithStatement 혹은 TryStatement의 Catch절과 같은 몇가지 특정 구문 구조와 연관되어 있습니다. 그리고 새로운 Lexical Environment는 이러한 코드들이 평가될 때마다 만들어집니다.
3. Execution Context 형태
실행 콘텍스트 형태
•
book() 함수가 호출되면
◦
show Function 오브젝트 생성
◦
show의 [[Scope]]에 스코프 설정
•
show() 함수가 호출되면 EC 생성
◦
함수 실행을 위한 Context 환경 구축
◦
LEC, VEC, TBC 생성 첨부
◦
LEC에 ER, OLER 첨부
◦
ER에 DER, OER 첨부
•
DER에 show()의 변수, 함수 기록
•
OLER에 show의 [[Scope]]를 설정
•
this 바인딩 컴포넌트에this참조 설정
대괄호 2 개 [[ ]] 는 엔진이 설정하는 Property를 의미합니다.
LEC: Lexical Execution Context
VEC: Variable Execution Context
TBC: This Binding Component
ER : Environment Record
DER: Declaration Environment Record
show 실행 콘텍스트(EC){
렉시컬 환경 컴포넌트(LEC):{
환경 레코드(ER): {
선언적 환경 레코드(DER): {
title: "JS책"
},
오브젝트 환경 레코드(OER): {}
},
외부 렉시컬 환경 참조(OLER): {
point: 123,
getPoint: function(){}
}
},
변수 환경 컴포넌트(VEC): {},
this 바인딩 컴포넌트(TBC): {
글로벌 오브젝트(window)
}
}
Plain Text
복사
function book(){
var point = 123;
function show(){
var title = "JS책";
//getPoint();
//this.bookAmount
};
function getPoint(){
return point;
};
show();
};
book();
JavaScript
복사
4. 식별자 해결, 스코프 용도
식별자 해결(Identifier Resolution)
사용할 변수/함수를 결정하는 것
신속하고 정확한 검색을 위해 스코프가 필요합니다.
우측의 코드를 살펴봅시다. getPoint메소드를 호출해 출력하는 코드인데, 결과로 무엇이 출력될까요? 정답은 200입니다. (1) 항목에서 getPoint메소드가 호출 되면 getPoint()메소드의 내부로직이 수행되는데 이 로직부분을 스코프(scope)라 합니다. 이부분에서 point에 200을 할당한 뒤 point를 반환하는데, 이 때 자바스크립트 엔진에서는 해당 getPoint 엔진에서 이름이 point인 식별자를 찾습니다. 해당 로직에서는 var point =200을 찾았으니 해당 변수값을 반환했지만, point가 스코프내에 선언이되어있지 않았다면 그 위의 스코프에서 해당 이름(point)으로 식별자를 찾습니다. 그래서 만약 getPoint 메소드에서 point 변수 선언을 하지 않았다면 반환되는 값은 한단계 위에 스코프에 있는 point값인 100이 반환될 것입니다.
var point = 100;
function getPoint(){
var point = 200;
return point;
};
var result = getPoint();//---(1)
console.log(result);
JavaScript
복사
•
스코프에서 이름은 값은 변경되지만, 이름은 변경되지 않습니다.
그렇기 때문에 식별자 해결(Identifier Resolution)대상은 이름이 됩니다.
스코프 용도
•
식별자 해결을 위한 수단이자 방법입니니다. 스코프가 목적이 아닙니다. 위 코드에서 point를 찾기위해 스코프를 사용했습니다. 만약, 식별자가 유일하게된다면 스코프는 따로 필요하지 않습니다. 하지만, 스코프를 유일하게 작성하는것은 불가능합니다. 어째서일까요?
프로그램을 작성하다보면 여러가지의 메소드들을 만들게됩니다. 그리고 그 안에는 각자 용도에 맞는 시맨틱 프로퍼티를 선언하게되는데, 이 시맨틱은 중복될 수 있습니다. 그렇기 때문에 계층적 구조로 만들어서 구분하는 것입니다.
5. scope chain, 스펙의 scope chain 사용
ES3: scope chain
scope chain은 실행 콘텍스트와 관련이 있고, 식별자 해결을 위해 사용합니다. 이 부분은 ES5와도 동일하지만, ES5는 scope는 사용하지만 scope chain은 사용하지 않습니다. 그리고 scope chain은 식별자를 검색하기 위한 key/value의 오브젝트 리스트입니다.( ex: {name: value})
정리하면,
1.
함수 호출
2.
스코프(scope)를 생성한 뒤 함수의 변수와 함수를 {name: value} 형태로 설정
3.
생성한 scope를 scope chain에 연결하고 식별자를 해결
이렇게 key/value 오브젝트 리스트로 계층적으로 만들고 그 안에서 식별자를 통해 해결(Identifier Resolution)을 하는데 중요한점은 이러한 동작을 동적으로 한다는 것입니다. 예를들어 point를 구하는 getPoint라는 함수를 호출하면 그 시점에 변수와 함수 이름을 프로퍼티로 만들고 scope chain에 연결된다는 것입니다.
반면, ES5는 정적환경(Lexical Environment)이기에 함수가 호출되었을 때 정적환경안에 변수와 함수를 설정하고 그 외의 처리는 하지 않습니다.
그렇기 때문에 하나의 정적환경에서 프로퍼티를 관리하는 ES5가 함수가 호출될 때마다 새로 프로퍼티를만들고 scope chain에 연결하는 ES3보다 속도가 빠릅니다.
•
ES3의실행 콘텍스트 환경은 scope chain과 Activation Object (함수가 실행될 때 실행될 결과와 실행되기 위한 환경을 만드는 것)가 있는데 ES5에서는 scope chain은 없고 Activation Object에 대응하는 Lexical Environment가 있습니다. 이는 아키텍처구조로 볼 때 동적 vs 정적이라는 큰 아키텍처 구조적 차이가 있습니다.
•
스펙별 scope chain 사용 횟수
◦
ES3: 37
◦
ES5: 1
◦
ES6: 0
•
ES5에서 한 번 사용한것도 바뀐 것을 나타내기 위해 사용한 것으로 Lexical Environment의 선언적 환경 레코드(Declarative Environment Record)로 대체하기위해 함수의 변수와 함수 이름을 바인드할 때 입니다. 그래서 ES5에서는 scope chain을 사용하지 않으며 DER에서 변수와 함수 이름을 검색합니다.
6. Lexical Environment, var 키워드 문제와 해결, 동적 환경
Lexical Environment
ES5부터 사용되는 정적환경(Lexical Environment)은 function 키워드를 만나면 function 오브젝트를 생성하고 스코프를 FO의 [[Scope]]에 설정합니다.
즉, 함수 밖의 scope가 설정되는 것입니다. (함수 안은 아직 들어가지 않았기에 모릅니다.) 이 시점에서 스코프가 결정됩니다.
즉, 함수를 호출할 때가 아니라 function을 만났을때 스코프가 결정되는데 이것이 Lexical Environment입니다. 또한, 함수가 호출되면 FO의 [[Scope]] 실행 콘텍스트의 Lexical Environment Component의 outer Lexical Environment 참조에 설정됩니다.
여기 Lexical Environment안에는 선언적 환경 레코드(Declarative Environment Record)가 있는데 함수가 호출되었을 때 함수안에 작성된 변수와 함수가 들어있습니다. 따라서 함수밖에있는 함수와 변수, 그리고 함수안에있는 함수와 변수를 하나의 정적 환경에서 사용할 수 있습니다.
var 키워드 문제
var point = 0;
function getPoint(){
var outerPoint;
function innerGetPoint(){
point = 100;
return point;
}
return point;
}
JavaScript
복사
함수에서 var 키워드를 사용하지 않고 변수를 선언하게되면 글로벌 오브젝트로 설정이되는데 이는 Lexical Environment Structure에 맞지 않습니다.
위 코드에서 point를 var 키워드를 사용하지 않고 사용하게되면 함수 밖에 있는 point를 가지고 와야하기 때문이죠.
•
ES5 해결 방법
◦
"use strict" 사용을하여 엄격한 문법을 강제하도록하여 애초에 에러가 발생하도록 합니다.
•
ES6 해결 방법
◦
let변수, const변수
◦
변수 자체에 스코프 제약을 둡니다.
동적 환경
자바스크립트에선 실행하는 시점에 스코프가 결정되는 경우도 있습니다.
•
with문
•
eval() 함수
두 가지가 있는데 with문은 strict 모드에서는 에러를 발생합니다. 그리고 eval()함수는 보안에 문제가있어 사용하는걸 지양해야 합니다.
7. Node.js 코드 형태
Node.js를 통해 서버 프로그램을 구현할때 고려해야할 사항으로는 동시접속자가 있는데 고려 접속자가 10K 이상일 경우를 고려해야 합니다.
한편, JS는 Single thread이기에 하나의 작업이 끝난 뒤 다음 작업이 수행되는 순차적인 처리를 하게되는데 이를 동기처리라 합니다.
그런데, Node.js에서 JS는 비동기 처리를 하기에 하나의 로직 수행중 다른 로직도 수행될 수 있다는 것이죠.
이런 비동기 처리는 C++의 Semapore와 Mutex때문인데, 하나의 프로세스가 실행되는 도중 Idle time이 생기면 wait que에 있던 프로세스를 실행시킵니다. 즉 비동기처리를 하게되는것입니다.
다시 본론으로 돌아와 이런 비동기 상황에서는 Context형태가 효율성이 높습니다. 예를들어 ES3는 scope chain과 activation object가 메모리에 올라가는데, 실행 도중 다른 함수가 호출될 경우에도 scope chain과 activation object가 올라오게 됩니다. 그렇게되면 두개가 서로 번갈아가며 수행될텐데, 이게 10K이상의 동시접속자가 있는 환경에서는 문제가 될 수 있습니다.
반면, ES5에서는 실행 콘텍스트(Execution Context)가 하나입니다. 그렇기에 함수가 실행되는 도중에 다른 함수가 호출되더라도 하난의 컨텍스트만 올라가있는 상태이기 때문에 ES3보다 엔진처리부분에서 성능이 더 뛰어날 것입니다.