Javascript

[javascript] object의 property에 대하여(configurable, enumerable, writable)

tea-tea 2024. 3. 23. 19:32

흔히 알려진 object의 정보


자바스크립트의 object에 대해 일반적으로 많이 하는 정보는 다음과 같다.

  • 넓은 정의 | 원시 데이터 값이 아닌 모든 데이터 (array, object, function ...)
  • 좁은 정의 | property로 구성된 집합
  • property | property는 key와 value의 쌍으로 이뤄져 있고 value는 모든 데이터 타입이 가능하다. 그 중, 함수를 value로 삼은 property를 특별히 method라 부른다.
  • 생성 방법 | 오브젝트 리터럴({})을 사용하거나 Object 함수를 사용한다.

이걸로 object를 다 안다고 말할 수 있을까?

 

prototype


우리는 object의 property를 참조할 때, obj.proper1 이나 obj[proper1] 같은 방식을 사용한다. 그런데, 우리가 만드는 object에는 method를 정의하지 않았음에도 기본적으로 사용가능한 것들이 존재한다.(toString() 같은 것들) 이런 것들은 어디서 온 것들일까.

 

우리는 객체를 간단히 중괄호 표기법, 즉 오브젝트 리터럴로 쉽게 생성하곤 하지만, 이것의 실제 동작 방식은 new Object()라는 생성자 함수를 통해 만들어진다. 이렇게 생성자 함수를 통해 만들어진 객체를 인스턴스라 한다. 인스턴스는 생성자 함수의 this에 바인딩되어 만들어지며 내부 값을 복사하지만, prototype object를 공유한다. 그리고 prototype object를 통해서 최상위 생성자 함수인 Object를 참조할 수 있다. 우리가 아무렇지 않게 사용하는 객체 메소드는 사실 js에서 그 메소드를 찾아 prototype link를 타고 Object를 참조함으로써 사용가능한 것이다. 이를 prototype chain이라 부른다.

실제로 obj.__proto__ 라는 property를 이용하면 인스턴스는 생성자 함수의 prototype object를 참조할 수 있다.

 

property의 세 가지 특성


그런데, 이상한 게 있다. 윗 내용에 따르면 , 우리가 만든 object는 인스턴스이고 기본적으로 __proto__ 속성이 장착되어 있다는 건데, 왜 for ...in 루프나 Object.key()에서는 해당 속성을 꺼내올 수 없을까?

 

사실, 같아보이는 property라도 특성이 다를 수 있다. 이 특성은 세 가지로

열거가능(enumerable),쓰기가능(writable), 구성가능(configureable)이 있다. 사실, 우리가 편하게 사용하는 오브젝트 리터럴은 이런 속성들을 기본값으로 자동 설정해주고 있었고, 일부 속성들은 이 특성이 비활성화되어 Object.key()같은 일부 메소드나 statement에서 탐색할 수 없던 것이었다.

 

세 가지 특성 조작해보기


오브젝트의 프로퍼티를 추가하거나 변경할 때 잘 모르는 방법 중 하나로 Object.defineProperty()가 있다.

//Object.defineProperty(obj, prop, descriptor);
const obj1 = {};
Object.defineProperty(obj1, "foo", { value: 20,enumerable:true });

console.log(obj1);
// {foo:20}

console.log(Object.keys(obj1));
// []

 

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

 

위 메소드는 객체의 property를 추가할 때 앞서 말한 세 가지 특성을 조작할 수 있다.

각 특성은 다음 사항을 결정한다.

  • configurable | 속성의 값을 변경하거나 delete 할 수 있으면 true이다. 기본 값은 false.
  • enumerable | 속성이 객체의 속성 열거 시 노출되면 true이다. 기본 값은 false.
  • writable | 할당 연산자(=)로 속성의 값을 바꿀 수 있으면 true이다. 기본 값은 false

이제 감이 온다. 오브젝트 리터럴은 프로퍼티의 세 가지 특성을 자동으로 true로 설정한다. 이런 설정을 잘 이용하면, 자바스크립트 객체의 문제 중 하나인, 클라이언트단에서 수정이 쉽다는 점을 해결할 수 있을지도 모른다.

(하지만, 지금의 나에게는 먼 얘기이다.)

 

적어도 enumerable 은 Object.key()나 for in 루프에서 왜 prototype을 참조할 수 없는지 알려준다. 이 property가 열거가능한 속성이 아니기 때문이다.

추가로, Object.assign() 역시 소스의 열거가능한 속성만을 복사하므로, prototype까지 복사할 수는 없다.

 

참고

for in 루프의 경우, 대상이 되는 오브젝트는 그 자체의 property 뿐만 아니라 prototype object 내 property까지 참조하며 순회한다. 따라서, 이를 원치않는다면 Object.prototype.hasOwnProperty(key)로 지금 순회 중인 property가 원래 객체 자체의 것인지 파악가능하다.