코어자바스크립트 : 프로퍼티 플래그와 설명자

프로퍼티 플래그

객체 프로퍼티는 값(value)에 더해 플래그(flag)라 불리는 특별한 속성 세가지를 갖는다.

  • writabletrue이면 값을 수정할 수 있습니다. 그렇지 않다면 읽기만 가능합니다.
  • enumerable - true이면 반복문을 사용해 나열할 수 있습니다. 그렇지 않다면 반복문을 사용해 나열할 수 없습니다.
  • configurabletrue이면 프로퍼티 삭제나 플래그 수정이 가능합니다. 그렇지 않다면 프로퍼티 삭제와 플래그 수정이 불가능합니다.

평범한 방식으로 프로퍼티를 만들면 위 플래그 속성은 모두 true 값을 갖는다.

Object.getOwnPropertyDescriptor

Object.getOwnPropertyDescriptor 메서드로 특정 프로퍼티에 대한 정보를 모두 얻을 수 있다.

let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);

위 메서드를 호출하면 프로퍼티 설명자(descriptor)라 불리는 객체가 반환된다. 말 그래도 프로퍼티의 값과 세 플래그에 대한 정보를 설명해준다.

let user = {
  name: "John"
};

let descriptor = Object.getOwnPropertyDescriptor(user, 'name');

alert( JSON.stringify(descriptor, null, 2 ) );
/* property descriptor:
{
  "value": "John",
  "writable": true,
  "enumerable": true,
  "configurable": true
}
*/

Object.defineproperty

플래그를 변경할 수 있다. 이 메서드는 객체에 해당 프로퍼티가 있으면 플래그를 원하는대로 변경해준다. 프로퍼티가 없으면 인수로 넘겨받은 정보를 이용해 새로운 프로퍼티를 만든다. 이때 플래그 정보가 없으면 플래그 값은 자동으로 false가 된다.

Object.defineProperty(obj, propertyName, descriptor)

프로퍼티 name이 만들어지고, 모든 플래그 값이 false가 되었다.

let user = {};

Object.defineProperty(user, "name", {
  value: "John"
});

let descriptor = Object.getOwnPropertyDescriptor(user, 'name');

alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
  "value": "John",
  "writable": false,
  "enumerable": false,
  "configurable": false
}
 */

writable 플래그

기존의 프로퍼티에 writable 플래그를 false로 변경

let user = {
  name: "John"
};

Object.defineProperty(user, "name", {
  writable: false
});

user.name = "Pete"; // Error: Cannot assign to read only property 'name'

defineProperty 메서드로 새로운 프로퍼티를 만들때, writable 플래그를 명시하지 않았으니 동일하게 false가 되었다.

let user = { };

Object.defineProperty(user, "name", {
  value: "John",
  // defineProperty를 사용해 새로운 프로퍼티를 만들 땐, 어떤 플래그를 true로 할지 명시해주어야 합니다.
  enumerable: true,
  configurable: true
});

alert(user.name); // John
user.name = "Pete"; // Error

enumerable 플래그

특정 프로퍼티의 enumerable 플래그 값을 false로 설정하면 for..in 반복문에 나타나지 않게 할 수 있습니다. 커스텀 toString도 열거가 불가능하게 할 수 있습니다.

let user = {
  name: "John",
  toString() {
    return this.name;
  }
};

Object.defineProperty(user, "toString", {
  enumerable: false
});

// 이제 for...in을 사용해 toString을 열거할 수 없게 되었습니다.
for (let key in user) alert(key); // name

열거가 불가능한 프로퍼티는 Object.keys에도 배제됩니다.

alert(Object.keys(user)); // name

응용하기

Object.defineProperties

Object.defineProperties(obj, descriptors) 메서드를 사용하면 프로퍼티 여러 개를 한 번에 정의할 수 있다.

문법:

Object.defineProperties(obj, {
  prop1: descriptor1,
  prop2: descriptor2
  // ...
});

예시:

Object.defineProperties(user, {
  name: { value: "John", writable: false },
  surname: { value: "Smith", writable: false },
  // ...
});

Object.getOwnPropertyDescriptors

Object.getOwnPropertyDescriptors(obj) 메서드를 사용하면 프로퍼티 설명자를 전부 한꺼번에 가져올 수 있다.

이 메서드를 Object.defineProperties와 함께 사용하면 객체 복사 시 플래그도 함께 복사할 수 있다.

let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));

주로 아래와 같이 할당 연산자로 프로퍼티를 복사하는 방식으로 객체를 복사하는데. 이 방법은 플래그는 복사하지 않는다. 플래그 정보도 복사하기 위해서는 Object.defineProperties를 사용하자.

for (let key in user) {
  clone[key] = user[key]
}

위 샘플 코드처럼 for..in을 사용해 객체를 복사하면 심볼형 프로퍼티도 놓치게 되지만 Object.getOwnPropertyDescriptors는 심볼형 프로퍼티를 포함한 프로퍼티 설명자 전체를 반환한다.

profile
(~˘▾˘)~♫❝ 쉽게만 살아가면 재미없어 빙고 .ᐟ ❞•*¨*•.¸¸♪

0개의 댓글