JavaScript 완벽 가이드, ECMAScript 을 읽는 방법
저번 글 에서는 ECMAScript 를 읽기 위해 알아야 할 개념들을 소개했고, 이번 글에서는 ECMAScript 문서에서 자바스크립트 동작 원리를 파악하는 과정을 소개하도록 하겠습니다.
const o1 = { foo: 99 };
const o2 = {};
Object.setPrototypeOf(o2, o1);
o2.foo;
// → 99
위 코드를 보면 Object.setPrototypeOf(o2, o1)
을 호출해서 o2
의 프로토타입을 o1
으로 설정했습니다. o2
의 프로토타입이 o1
이 되었기 때문에 o2
에 foo
라는 속성이 없어도 o2.foo
는 o1.foo
에 접근하여 값을 얻어 마지막 코드 o2.foo
의 결과가 o1.foo
의 값과 동일한 99
가 됩니다.
위와 같은 동작을 자바스크립트 용어로 프로토타입 체인 이라 하며, 프로토타입 체인의 원리는 ECMAScirpt 문서에 정의되어 있습니다.
프로토타입 체인
저번 글의 'Ordinary' Object vs 'Exotic' Object 문단에서 설명했듯이, 모든 Object 는 ECMAScript 에 정의된 Essential Internal Methods 를 가지고 있습니다. Essential Internal Methods 중 프로토타입 체인의 동작 원리와 관련하여 자세히 살펴봐야 하는 함수는 [[Get]]
입니다. [[Get]]
함수는 Object 의 속성에 접근할 수 있는 함수입니다. 그리고 Ordinary Object 는 [[Prototype]]
이라는 Internal Slot 을 가지고 있습니다. 자바스크립트의 프로토타입 체인은 Internal Methods [[Get]]
과 Internal Slot [[Prototype]]
을 이용해서 동작합니다. 위에서 제공한 예제 코드을 바탕으로 구체적으로 설명드리도록 하겠습니다.
먼저 o2.foo;
는 개발자가 작성한 문자열에 불과합니다. o2.foo;
문자열은 ECMAScript 문서에 정의된 대로 자바스크립트 엔진에 의해 해석되고 ECMAScript 에 정의된 절차대로 동작해야 의미를 가집니다. 그렇다면 자바스크립트는 문자열 o2.foo;
를 어떻게 해석할까요?
ECMAScript 문서 중 자바스크립트 문법의 표현을 어떻게 받아들일지를 정의한 Expressions 에 따르면 o2.foo;
는 'MemberExpression.IdentifierName' 형태로 MemberExpression 에 해당합니다. o2['foo'];
도 'MemberExpression.[Expression]' 형태이기 때문에 MemberExpression 에 해당합니다.
자바스트립트 엔진은 MemberExpression 을 Runtime Semantics 에 정의된 절차대로 진행하게 됩니다. Runtime Semantics 은 해석된 문법을 런타임에 어떻게 처리할지를 절차로 정의한 내용이고, MemberExpression 에 대한 처리 절차는 위 그림과 같습니다. 위 절차 중 2번 절차 'Let baseValue be ? GetValue(baseReference)' 를 따라가면, Object 의 Internal Methods 중 하나인 [[Get]](P, Recevier)
함수를 호출하게 됩니다.
GetValue
함수에 전달하는 baseReference
는 Specification Types 중 하나인 Reference Record 타입으로 다음 표와 같은 속성을 가집니다.
Field Name | Meaning |
[[Base]] | Environment Record 로, 클로저의 Lexical Environment 와 관계된 데이터입니다. 'o2.foo;' 의 경우 전역 환경에서 실행되므로 Global Environment Record 가 됩니다. |
[[ReferencedName]] | 참조 이름을 표현한 String 데이터로, 'o2.foo;' 의 'foo' 입니다. |
[[Strict]] | 자바스크립트가 Strict 모드에서 실행되는지를 Boolean 으로 나타나는 데이터입니다. 'o2.foo;' 의 경우 'false' 입니다. |
[[ThisValue]] | 자바스크립트에서 사용하는 'this' 에 대한 데이터입니다. 'o2.foo;' 에서 'o2' 입니다. |
MemberExpression 을 처리하는 절차 중 호출한 GetValue(V)
절차 중 4.b 번에서 Object baseObj
의 속성을 접근하는 Internal Methods 인 [[Get]](P, Recevier)
를 호출합니다.
[[GetValue(V)]]
의 절차 중 4.b 에서 'GetThisValue(V)' 함수를 호출하고 있습니다. 'GetThisValue(V)' 은 자바스크립트에서 자주 사용되는 this
를 결정해서 반환하는 함수이며, Object 의 Internal Methods 인 [[Get]](P, Recevier)
의 Receivier
에 해당합니다.
지금까지 o2.foo;
에 대한 문법 해석부터 Object 의 [[Get]](P, Recevier)
함수를 호출했는지를 살펴봤습니다. 이제 자바스크립트에서 Object 의 속성을 어떻게 찾아서 반환하는지를 살펴봅시다.
먼저 [[Get]](P, Recevier)
함수 정의를 보면 Property Key 인 P
와 Receiver
를 변수로 받아, OrdinaryGet(O, P, Receiver)
을 호출하고 결과를 반환합니다. [[Get]](P, Recevier)
함수의 P
는 GetValue
함수를 호출할 때 전달한 V.[[ReferencedName]]
즉 foo
가 되고, Receiver
의 경우 GetThisValue(V)
의 반환값인 o2
가 됩니다.
OrdinaryGet(O, P, Receiver)
함수는 2번 절차에서 원하는 속성이 있는지를 확인하고 원하는 속성이 없으면, 3번 절차를 진행합니다. 3번 절차에서는 O.[[GetPrototyupeOf]]()
를 호출해 Internal Slot 인 [[Prototype]]
을 얻어서 [[Prototype]]
의 [[Get]](P, Receiver)
를 호출합니다.
o2.foo;
를 가지고 설명하자면,
o2
의 OrdinaryGet(O, P, Receiver)
호출에선 o2
에 foo
속성이 없으므로 3번 절차를 진행하게 되고, o2
의 프로토타입인 o1
의 [[Get]](P, Receiver)
를 호출합니다.
o1
의 OrdinaryGet(O, P, Receiver)
호출에선 o1
에 foo
속성이 있으므로 o1.foo
값인 NormalCompletion(99)
를 반환합니다.
o2
의 OrdinaryGet(O, P, Receiver)
는 o1
의 OrdinaryGet(O, P, Receiver)
호출 결과인 NormalCompletion(99)
을 반환하고 종료합니다.
결과
결국 자바스크립트의 프로토타입 체인 은 OrdinaryGet(O, P, Receiver)
에 정의되어 있다고도 할 수 있습니다.
만약 Ordinary Object 가 아닌 Exotic Object 라면 [[Get]](P, Recevier)
가 OrdinaryGet(O, P, Receiver)
를 호출하지 않고 다르게 동작할 수도 있습니다.
ECMAScript 문서에는 이 글에서 설명한 프로토타입 체인만이 아니라, 자바스크립트에 대한 많은 인사이트를 얻을 수 있습니다. 저 또한 이번에 글을 작성하면서 개념적으로만 알고 있던 자바스크립트에 대해 많은 걸 배울 수 있었습니다. ECMAScript 문서를 읽는게 당장 개발에 도움이 되지 않을진 몰라도, 프론트 엔드 개발자로 기본을 탄탄히 할 수 있는 문서이므로 꾸준히 읽어 나갈 예정입니다.
참고
How to Read the ECMAScript Specification
Understanding the ECMAScript spec
'Develop > Frontend 가이드' 카테고리의 다른 글
[FE] Typescript - Type Guard 타입 가드 (0) | 2021.05.05 |
---|---|
[FE] HTTP/2 - HyperText Transfer Protocol (Version 2) 을 자세히 알아보자 (0) | 2021.04.13 |
[FE] JavaScript 완벽 가이드, ECMAScript 를 읽는 방법 (1/2) (0) | 2021.04.02 |
[FE] 브라우저 렌더링 Browser rendering - 5단계 변경 'change' (4) | 2021.03.31 |
[FE] 브라우저 렌더링 Browser rendering - 4단계 페인팅 'paint' (0) | 2021.03.30 |
꾸준히 노력하는 개발자 "김예건" 입니다.