본문 바로가기
JavaScript

[JavaScript] Property Attribute (프로퍼티 어트리뷰트)

by 배잼 2022. 2. 8.

모던 자바스크립트 Deep Dive 16장을 읽으며 정리한다.

property attribute

  • value (값)
  • writable (갱신 가능 여부)
  • enumerable (열거 가능 여부)
  • configurable (재정의 가능 여부)

자바스크립트 엔진은 property를 생성할 때 property의 상태를 나타내는 것들을 property attribute를 기본값으로 자동 정의. 상태란 위 네 가지를 뜻함.

property attribute는 meta-property인 내부 슬롯이다. 직접 접근 불가능, 간접 접근 가능

const fruit = {
	name : 'pear'
};

fruit.age = 10;

console.log(Object.getOwnPropertyDescriptors(fruit));

/*
age: {value: 10, writable: true, enumerable: true, configurable: true}
name: {value: 'pear', writable: true, enumerable: true, configurable: true}
*/

property의 분류 : data property / accessor property

  • data property : 키와 값으로 구성된 것 . 여태 했던 거다!
  • accessor property : 자체적으로는 값을 갖지 않는다. 다른 data proprty의 값을 읽거나 저장할 때 호출되는 accessor funtion으로 구성됨

data property의 property attribute

  • value (값) : property의 값으로 초기화된다
  • writable (갱신 가능 여부) : Boolean, false일 경우 값을 변경할 수 없다.
  • enumerable (열거 가능 여부) : Boolean, false일 경우 for... in이나 Object.keys 등으로 열거 불가능!
  • configurable (재정의 가능 여부) : Boolean, false일 경우 삭제나 값의 변경이 금지된다. 단, writable이 true 라면 value의 변경과 writable을 false로 변경이 허용된다.

accessor property의 property attribute

  • get : accessor property로 값을 읽을 때 호출되는 함수다. (getter 함수) 그 결과가 프로퍼티 값으로 반환된다.
  • set : accessor property로 값을 저장할 때 호출되는 함수다. (setter 함수) 그 결과가 프로퍼티 값으로 저장된다.
  • enumerable (열거 가능 여부)
  • configurable (재정의 가능 여부)

한번 accessor property를 살펴보자.

const fruit = {
  name: "pear",

  get fruitName() {
    return `${this.name}`; //getter 함수
  },
  set fruitName(fname) {
    [this.name] = fname.split(" "); //setter 함수
    //배열 디스트럭처링 할당 -> 추후 공부한다
  },
};

fruit.fruitName = "cherry";
console.log(fruit); // {name: 'cherry'}
console.log(fruit.fruitName); // cherry

console.log(Object.getOwnPropertyDescriptors(fruit, "name"));
// fruitName: {enumerable: true, configurable: true, get: ƒ, set: ƒ}
// name: {value: 'cherry', writable: true, enumerable: true, configurable: true}

console.log(Object.getOwnPropertyDescriptors(fruit, "fruitName"));
// fruitName: {enumerable: true, configurable: true, get: ƒ, set: ƒ}
// name: {value: 'cherry', writable: true, enumerable: true, configurable: true}

property 정의

앞서 봤던 property attribute를 명시적으로 정의할 수 있다. 바로 Object.defineProperty 를 쓰면 된다! Object.defineProperties를 쓰면 여러 개를 정의할 수 있다.만약 discripter 객체의 property를 누락시킨다면 undefined, false가 기본값이다. value, get, set의 경우는 undefined, 나머지는 false다.

const fruit = {
};

Object.defineProperty(fruit, 'name',{
    value : 'cherry'
});

console.log(Object.getOwnPropertyDescriptors(fruit))
//name: {value: 'cherry', writable: false, enumerable: false, configurable: false}

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

fruit.name = 'pear';
console.log(fruit); //{name: 'cherry'}, 변경되지 않는다. 

delete fruit.name; //사라지지 않는다.

그렇다면 객체의 변경을 방지하는 방법은 무엇일까?

  1. Object.preventExtensions , 객체 확장 금지. 프로퍼티 추가가 금지된다.
    • Object.preventExtensions() 안에 객체를 넣으면 프로퍼티 추가가 금지된다.
    • Object.isExtensible()을 사용하면 확장 금지인지 알 수 있다.
    • 추가는 금지되지만 삭제는 가능하다.
  2. Object.seal , 객체 밀봉. 프로퍼티 추가 및 삭제, property attribute 정의가 금지된다.
    • 밀봉된 객체는 읽기와 쓰기만 가능하다.
    • 즉, 값 갱신은 가능하다.
    • 그 여부는 Object.isSealed method로 확인 가능하다.
  3. Object.freeze , 객체 동결. 프로퍼티 추가 및 삭제, property attribute 재정의 금지, property 값 갱신 금지.
    • 동결된 객체는 읽기만 가능하다.
    • Object.isFrozen 메소드로 확인 가능하다.

하지만 중첩 객체라면 어떨까? : 불변 객체

위 방법은 직속 proerty만 변경이 방지되고, 중첩 객체까지는 영향을 주기 어렵다. 중첩 객체까지 변경이 불가능하게 하려면 어떻게 해야 할까?

: 객체를 값으로 갖는 모든 property에 대해 재귀적으로 Object.freeze method를 호출해야 한다.

다음과 같은 함수를 작성할 수 있다.

function Freeze(obj){

    if (obj && typeof obj === 'object' && !Object.isFrozen(obj)){
        Object.freeze(obj);
        Object.keys(obj).forEach(key => Freeze(obj[key]));
    }

    return obj
}

const fruit = {
    name : 'cherry',
    profile : { year : '1900'}
}

console.log(Object.isFrozen(fruit)); //false
console.log(Object.isFrozen(fruit.profile)); //false

Freeze(fruit);

console.log(Object.isFrozen(fruit)); //true
console.log(Object.isFrozen(fruit.profile)); //true

fruit.profile.year = '2000'
console.log(fruit); 
// name: "cherry"
// profile: {year: '1900'}

댓글