[Javascript] 다시 공부하는 Object

다시 공부하는 Javascript Object

Javascript의 객체를 다시 공부해보기로 했다. 객체를 공부하는 데에 납득을 하면서도 깔끔하게 내가 알고 있다는 느낌을 받지 못했었던 것 같다. 그래서 어떻게 하면 좀 더 이 먹구름을 파헤치는 것 같은 함수(객체) 공부를 명확히 할 수 있을까……하다가 그냥 결론낸게 명세서 처음부터 보기이다…..

너무 무식한 방법일 수 있겠지만, 의외로 구글 번역도 깔끔하게 잘되는 것 같아서 되는 데까지 도전해보기로 했다. 어떻게 보면 당연한 얘기를 다시 길게 풀어쓰는 느낌으로 될 수도 있겠다. 하지만 확실한건 명세서보다 더 자세히 설명되어있는 자료는 없는 것 같다..!!! 객체는 어떻게 구성되어있는지부터 차근차근 정리해보겠다. 이 시간이 의미있는 시간이 되었으면 좋겠다.

자바스크립트에서 함수는

남들이 다 아는 자바스크립트 함수부터 거슬러 올라가봅시당…

자바스크립트에서 함수는 매우 중요한 개념이다. 함수는 일급 객체이다. 즉, 함수는 다른 객체들과 똑같이 또 다른 함수의 인자로 들어갈 수도 있고, 함수를 리턴할 수도 있다.

function은 공통적으로 [[constructor]]라는 내부 메소드를 가지고 있다. 또한 [[constructor]]를 가지고 있는 객체는 [[call]]이라는 내부 메소드 또한 가지고 있는데, 이는 즉, 모든 constructor가 있는 객체는 함수라는 뜻이다.

다시 말하면, constructor를 내부적으로 가지고 있는 아이는 모두 함수이다. 그 반대는 아님..

또한 해당 function 안에 있는 constructor로 객체(인스턴스)를 생성하는데, 해당 객체는 속성 참조를 확인하기 위해 암시적으로 function constructor의 prototype 프로퍼티를 참조한다. constructor의 prototype 프로퍼티란, 간단하게 Function.constructor.prototype을 의미하는데, 이를 통해서 인스턴스(객체)가 어떤 prototype을 공유하고 있는지, 상속 여부를 알 수 있다.

모든 객체는 prototype을 공유하고 있다. 대체적으로 새로운 객체가 만들어지면, Object.create라는 내장 함수를 사용하여 명시적으로 지정된 프로토타입을 공유하고 있다. 그렇다면 좀 더 올라가서…. 객체를 생성할 때, 어떻게 무엇이 생성되고 이는 어디에 쓰이는가?? 우선 객체라는 것이 기본적으로 가지고 있는 프로퍼티(속성)부터 정리해보았다.

Object Type

하나의 객체는 논리적인 프로퍼티 모음을 가지고 있다. 각 프로퍼티는 data propertyaccessor property로 나뉘어진다.

  • data property의 경우, 이는 원시타입으로 이루어진 key, value와 Boolean 속성으로 연관되어있다.
  • accessor property는, 두 개의 접근 함수(accessor functions)와 Boolean 속성 집합으로 이루어져있는데, 그 접근 함수들은 프로퍼티와 관련있는 원시적인 타입 값을 저장하거나 검색하는 데에 사용된다.

Data Property

  • [[Value]] : 해당 값에 접근하여 값을 가져와서 조회하는 경우에 쓰인다.
  • [[Writable]] : 해당 프로퍼티는 Boolean이며, false일 경우 데이터를 조회([[Value]])하는 것은 가능하지만, [[Set]]하는 것은 불가능하다.
  • [[Enumerable]] : 해당 값 또한 Boolean이다. true일 경우 해당 프로퍼티가 for-in 연산자로 하여금 확인할 수 있다. 그렇지 않으면, 해당 프로퍼티는 셀 수 없는 프로퍼티라고 한다.
  • [[Configurable]] : 이 또한 Boolean으로 구성된다. 만약 false일 경우, 프로퍼티를 지우려고 시도할 때, data 자체는 지우는 것이 가능하지만, attributes 자체는 실패할 것이다.

Accessor Property

  • [[Get]] : 이 Attribute는 function 객체여야 한다. function 내부 메소드로 존재하는 [[Call]]이라는 것으로 인해 빈 arguments list로 프로퍼티를 접근하여 해당 메소드로 접근하여 수행한다.
  • [[Set]] : 이 Attribute 또한 function 객체여야 한다. 같은 방법으로 해당 메소드에 접근하는데, 해당 메소드로 내부적인 수행을 할 수 있지만, 권장되지는 않는다. 접근자로는 오직 [[Get]]만 수행하는 것을 추천하고 있다.
  • [[Enumerable]] : Boolean으로 구성되며, true인 경우 해당 프로퍼티가 for-in 연산자로 하여금 확인할 수 있다. 그렇지 않으면, 해당 프로퍼티는 셀 수 없는 프로퍼티라고 한다.
  • [[Configurable]] : 이 또한 Boolean으로 구성된다. 만약 false일 경우, 프로퍼티를 지우려고 시도할 때, data 자체는 지우는 것이 가능하지만, attributes 자체는 실패할 것이다.

Screen Shot 2020-02-02 at 9 15 49 PM

Object의 내부 메소드와 Internal Slots

기본 객체는, 내부 메소드라는 것이 지정되어 있다. ECMAScript 엔진에서 각 객체는 런타임 동작을 정의하는 내부 메소드와 연관되어 있다. 이 내부 메소드는 ECMAScript 언어의 일부가 아니다. 이들은 순수하게 설명 목적으로 정의된 것이다. 하지만, 객체들은 이 내부 메소드들의 지정한 대로 작동해야 한다. 이 또한 구현의 방식에 따라 다르게 실행될 것이다.

즉, 내부 메소드들의 동작 방식은 당시의 target에 따라 다르게 실행되는데, 이는 만약 지원해주지 않는 객체에서 내부 메소드를 사용하려고 시도할 때, TypeError를 뿜을 수 있다.

여기서 Internal Slots이 객체와 직접적으로 연관되어있는 내부 상태에 해당한다. Internal Slots는 내 객체의 프로퍼티가 아니고, 그들은 상속되어있는 존재가 아니다. 특정 내부 슬롯(Internal Slots)의 사양에 따라, 원사 타입이나 특정 유형 값의 상태가 다를 수 있다. 명시적으로 지정하지 않는 한, 내부 슬롯은 객체 생성 프로세스의 일부로 할당되고, 이는 동적으로 추가되지 않을 것이다. 내부 슬롯의 초기 값은 지정하지 않는다면 undefined이다. 이 내부 슬롯이 있는 객체를 생성할 때, 다양한 알고리즘이 존재한다. 하지만, 해당 객체에 직접적으로 내부 슬롯을 접근할 수 있는 방법은 제공하지 않는다.

내부 메소드와 내부 슬롯은 [[name]] 이런 형태로 지정되어 있다. 그렇다면 객체가 생성될 때, 내부적으로 꼭 가지고 있어야하는 메소드는 무엇이 있을까??

우선 내부 메소드로 필수적으로 가지고 있어야하는 것은, ECMAScript 코드로 작성, 조작하는 모든 객체에 적용할 수 있는 필수 내부 방법이라고도 할 수 있다. 모든 객체는 이런 필수적인 내부 메소드를 돌릴 알고리즘을 가지고 있다. 하지만 모든 객체게 완전히 똑같은 알고리즘을 가지고 있어야하는 것은 또 아니다…(…)

Essential Internal Methods

  • [[GetPrototypeOf]] : 이 객체가 상속받은 프로퍼티를 제공하는 객체(부모객체)가 무엇인지 보여준다. null 값이 나올 경우, 상속받은 객체가 없다는 것을 가리킨다.
  • [[SetPrototypeOf]] : 상속할 프로퍼티를 가진 다른 객체를 해당 객체에 연결짓게 해주는 메소드이다. Boolean을 return하고, true가 나오면 성공적으로 상속되었음을 가리킨다.
  • [[IsExtensible]] : Boolean을 return한다. 해당 객체에 추가적으로 속성을 추가할 수 있는지 확인하는 메소드이다.
  • [[PreventExtensions]] : 이 메소드 또한 Boolean을 return하는데, 새로운 프로퍼티(속성)를 해당 객체에 추가했을 때, 해당 동작이 성공했으면 true를 리턴한다.
  • [[GetOwnProperty]] : property의 key 값을 받아서, 해당 키 값의 Property Descriptor을 return 한다. 만약 해당 property의 키값이 존재하지 않으면 undefined를 리턴한다.
  • [[DefineOwnProperty]] : 이미 존재하는 프로퍼티를 수정하거나 새로 만드는 작업을 수행할 때 쓰인다. 인자로 property key값과 세부적인 내용을 담는다(PropertyDescriptor). 해당 함수를 수행한 후 잘 동작됐으면 true를 반환한다.
  • [[HasProperty]] : 프로퍼티 키 값을 받아서 해당 객체가 이미 자신의 프로퍼티로 가지고 있거나, 상속된 프로퍼티로 가지고 있을 때, true를 return하도록 한다.
  • [[Set]] : 인자로 propertyKey, value, Receiver를 받는데, 해당 값으로 프로퍼티를 세팅할 때 쓰인다. propertyKey와 value로 해당 객체에서 프로퍼티를 셋팅하고, 마지막 Receiver로 this를 정한다. 그리고 해당 프로퍼티 설정이 성공했으면 true를 return한다.
  • [[Delete]] : 해당 객체에서 프로퍼티를 지울 때, propertyKey를 받아서 삭제한다. 삭제가 완료되었으면 true를 반환한다.
  • [[OwnPropertyKeys]] : 객체에서 가지고 있는 모든 프로퍼티 key들을 리스트로 return한다.

Additional Essential Internal Methods of Function Objects

객체가 함수일 때, 필수적으로 들어가야하는 내부 메소드들이다. 해당 함수 객체는 위에서 설명했듯이 실행시키기 위한 [[Call]] 내부 메소드가 존재한다. 그리고 constructor가 객체 내부에 존재하는 것은 [[Construct]]라는 내부 메소드가 존재한다. 즉, 모든 [[Construct]] 메소드가 있는 객체는 [[Call]] 또한 가지고 있다. 그리고 그 객체는 무조건 함수이다. constructor를 가지고 있는 것은 무조건 함수이다!!

  • [[Call]] : 객체 내부 코드를 실행시킬 때 사용하는 메소드이다. 함수 호출 표현식을 통해서 호출되는 메소드이다.(exFunc();) 이 메소드가 호출될 때 전달되는 인수는 this와 함수 객체에서 전달될 인수가 들어있는 arguments 객체를 리스트로 포함한다. 객체는 이 메소드를 통해서 호출할 수 있다.
  • [[Construct]] : 객체를 만드는 아이이다. new, super 연산자를 통해서 호출된다. 첫 번쨰 인자로는 해당 연산자로써 가지고 있는 arguments 리스트를 포함한다. 두번째 인자로는 new 연산자로 인해 처음 적용되는 대상이다. 객체는 이 메소드를 통해 constructors를 호출한다. 함수 객체는 꼭 constructor가 필요하진 않으며, constructor가 없는 함수 객체는 [[Construct]]라는 내부 메소드를 가지고 있지 않다.

느낀점

ECMA 표준 명세서를 제대로 본건 처음인 것 같다. 너무 방대한 양에 어떻게 줄여서 봐야할지 아직은 감이 잡히지 않아서 우선 읽히기 쉬운 Object 내부 메소드 리스트 부분부터 해봤던 것 같다. 사실상 그냥 명세서 번역을 한거지… 번역도 제대로 안되어있을듯.. 그래도 뭔가 명세서!하면 너무 무시무시하고 거대해보였는데 이번 기회로 억지로 번역해보니 생각보다 이런 공부를 하는 것도 재밌겠다는 생각이 들었다.

앞으로도 계속 명세서를 천천히 읽어나가면서 내가 원하는 정보를 스펀지처럼 쏘옥쏘옥 빨아들이면서 공부할 수 있게 되었으면 좋겠다. 많은 연습이 필요하겠지만… 나와 자바스크립트 사이의 커다랗고 두꺼운 벽을 좀 무너뜨리게된 좋은 기회였던 것 같다. 그래도 그래도 아직 명세서는 너무 어렵다…

참고