JavaScript

[JS] 모던 자바스크립트 - 11. 프로퍼티 어트리뷰트

개발자 조이 2021. 9. 5. 05:07
728x90
반응형

프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체


프로퍼티 어트리뷰트

- 프로퍼티의 상태를 나타내는 것

- 프로퍼티의 상태는 프로퍼티의 값(value), 값의 갱신 가능 여부(writable), 열거 가능 여부(enumerable), 재정의 가능 여부(configurable)을 말한다

- 자바스크립트 엔진이 프로퍼티를 생성할 때 프로퍼티 어트리뷰트를 기본값으로 자동 정의함 

- 자바스크립트 엔진이 관리하는 내부슬롯이므로 직접 접근 불가능

 

프로퍼티 디스크립터 객체

Object.getOwnPropertyDescriptor 메서드, Object.getOwnPropertyDescriptors 메서드로 간접적으로 프로퍼티 어트리뷰트를 확인 가능함

- 위 메서드는 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터 객체를 반환

- 존재하지 않는 프로퍼티나 상속받은 프로퍼티에 대한 프로퍼티 디스크립터를 요구 시 undefined 반환

const person = {
    name : 'Lee'
};

// 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터 객체를 1개를 반환
console.log(Object.getOwnPropertyDescriptor(person,'name'));

// { value : "Lee", writable : true, enumerable : true, configurable : true }

person.age = 20;

// 모든 프로퍼티의 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터 객체들을 반환
console.log(Object.getOwnPropertyDescriptors(person));

/*
{
    name : { value : "Lee", writable : true, enumerable : true, configurable : true },
    age : { value : 20, writable : true, enumerable : true, configurable : true }
}
*/

 

데이터 프로퍼티와 접근자 프로퍼티


- 프로퍼티는 데이터 프로퍼티와 접근자 프로퍼티로 구분

- 데이터 프로퍼티와 접근자 프로퍼티와를 구분하는 방법은 Object.getOwnPropertyDescriptor 메서드를 사용해 반환한 프로퍼티 디스크립터 객체를 확인하여 구분

// 일반 객체의 __proto__는 접근자 프로퍼티
Object.getOwnPropertyDescriptor(Object.prototype, '__proto__');
// { get : f, set : f, enumerable: false, configurable: true}

// 함수 객체의 prototype은 데이터 프로퍼티
Object.getOwnPropertyDescriptor(function() {}, 'prototype');
// { value: {...}, writble: true, enumerable: false, configurable: false};

 

데이터 프로퍼티

- 키와 값으로 구성된 일반적인 프로퍼티

- 프로퍼티 생성 시 [[Value]] 값은 프로퍼티 값으로 초기화, 나머지는 true로 초기화 

 

프로퍼티 어트리뷰트 프로퍼티 디스크립터 객체의 프로퍼티 설명
[[Value]] value - 프로퍼티 키로 프로퍼티 값에 접근하면 반환되는 값
- 프로퍼티 값 변경 시 [[Value]] 값 변경
[[Writable]] writable - 프로퍼티 값의 변경 가능 여부
- 불리언 값
- false인 경우 해당 프로퍼티의 [[Value]]의 값을 변경할 수 없는 읽기전용 프로퍼티가 됨
[[Enumerable]] enumerable - 프로퍼티의 열거 가능 여부
- 불리언 값
- false인 경우 해당 프로퍼티는 for...in문이나 Object.keys 메서드 등으로 열거X
[[Configurable]] configurable - 프로퍼티의 재정의 가능 여부
- 불리언 값
- false인 경우 해당 프로퍼티의 삭제, 프로퍼티의 어트리뷰트 값 변경 금지
- writable이 true인 경우 [[Value]]의 변경과 [[Writable]]을 false로 변경은 허용

 

접근자 프로퍼티

- 자체적인 값을 갖지 않고, 접근자 함수로 구성된 프로퍼티

- 접근자 함수는 다른 데이터의 프로퍼티의 값을 읽거나 저장할 때 사용하는 함수

- getter/setter 함수라고도 부름

- 메서드 앞에 get/set을 붙여서 사용

 

프로퍼티 어트리뷰트 프로퍼티 디스크립터 객체의 프로퍼티 설명
[[Get]] get - 데이터 프로퍼티의 값을 읽을 때 호출되는 접근자 함수
[[Set]] set - 데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 함수
[[Enumerable]] enumerable 데이터 프로퍼티의 [[Enumerable]]과 같음
[[Configurable]] configurable 데이터 프로퍼티의 [[Configurable]]과 같음

 

const person = {
    // 데이터 프로퍼티
    fistName : 'Heywon',
    lastName : 'Lee'

    // 접근자 프로퍼티
    // getter 함수
    get fullName() {
      return `${this.firstName} ${this.lastName}`;
    },
    // setter 함수
    set fullName(name) {
      [this.firstName, this.lastName] = name.split('');
    }
};

// 접근자 프로퍼티로 프로퍼티 값 저장
person.fullName = 'Hyewon Lee';
console.log(person); // {fistName: 'Hyewon', lastName: 'Lee'}

// 접근자 프로퍼티로 프로퍼티 값 참조
console.log(person.fullName); // Hyewon Lee

 

 

 

프로퍼티 정의


- 새로운 프로퍼티를 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의하는 것

- 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의하는 것

- Object.defineProperty 메서드로 프로퍼티 어트리뷰트 정의 가능

const person = {};

// 데이터 프로퍼티 정의
Object.defineProperty(person, 'firstName', {
    value : 'Hyewon',
    writable : true,
    enumerable : true,
    configurable : true
});

// 접근자 프로퍼티 정의
Object.defineProperty(person, 'fullName', {
    // getter 함수
    get() {
      return `${this.firstName} ${this.lastName}`;
    },
    // setter 함수
    set(name) {
      [this.firstName, this.lastName] = name.split('');
    },
    enumerable : true,
    configurable : true
});

- Object.defineProperty 메서드로 프로퍼티를 정의할 때 프로퍼티 디스크립터 객체의 프로퍼티를 일부 생략 할 수 있음

- 생략할 경우 기본값(value, get, set은 undefined / writable, enumerable, configurable은 false)으로 적용

- Object.defineProperties 메서드는 한번에 여러 개의 프로퍼티를 정의할 수 있음

const person = {};

Object.defineProperties(person, {
    // 데이터 프로퍼티 정의
    firstName: {
      value : 'Hyewon',
      writable : true,
      enumerable : true,
      configurable : true
    },
    lastName: {
      value : 'Lee',
      writable : true,
      enumerable : true,
      configurable : ture
    },
    // 접근자 프로퍼티 정의
    fullName : {
      // getter 함수
      get() {
        return `${this.firstName} ${this.lastName}`;
      },
      // setter 함수
      set(name) {
        [this.firstName, this.lastName] = name.split('');
      },
      enumerable : true,
      configurable : true
    }
});

 

객체 변경 금지


- 객체는 변경 가능한 값이므로 재할당 없이 직접 변경 가능

- 프로퍼티 추가 삭제 가능, 프로퍼티 값 갱신 가능, 프로퍼티 어트리뷰트 재정의 가능

- 이를 막기 위한 다양한 메서드가 있으며, 각각의 메서드는 객체의 변경을 금지하는 강도가 다름

 

Object.preventExtensions

- 객체 확장 금지

- 프로퍼티 추가 금지

- Object.defineProperty 메서드프로퍼티 동적 추가 방법 모두 금지

- 확장 가능 객체 여부는 Object.isExtensible 메서드로 확인 가능

- 추가는 금지되지만 프로퍼티 삭제는 가능

- 확장 금지 객체에 프로퍼티를 추가하면 에러 없이 무시됨, strict mode에서는 에러

 

Object.seal 

- 객체 밀봉

- 프로퍼티 추가 및 삭제프로퍼티 어트리뷰트 재정의 금지

- 읽기와 쓰기만 가능

- 밀봉 객체 여부는 Object.isSealed 메서드로 확인 가능

- 프로퍼티 값 갱신은 가능 (writable : true)

- 밀봉 객체에 프로퍼티를 추가 혹은 삭제 시 에러 없이 무시됨, strict mode에서는 에러

- 프로퍼티 어트리뷰트 재정의 시 에러

 

Object.freeze

- 객체 동결

- 프로퍼티 추가 및 삭제와 프로퍼티 어트리뷰트 재정의 금지, 프로퍼티 값 갱신 금지

- 읽기만 가능 (writable : false, enumerable : true, configurable : false)

- 동결 객체 여부는 Object.isFrozen 메서드로 확인 가능

- 동결 객체에 프로퍼티 추가,삭제, 값 갱신 시 에러 없이 무시 됨, strict mode에서는 에러

- 프로퍼티 어트리뷰트 재정의 시 에러

 

불변 객체

- 위의 메서드들은 얕은 변경 방지직속 프로퍼티만 변경이 방지되고 중첩 객체까지는 영향을 주지 못함

- 중첩 객체까지 동결하여 변경이 불가능한 읽기 전용의 불변 객체를 구현하려면 객체를 값으로 갖는 모든 프로퍼티에 대해 재귀적으로 Object.freeze 메서드를 호출해야함

function deepFreeze(target) {
    // 객체가 아니거나 이미 동결된 객체는 무시, 객체이고 동결되지 않은 객체만 동결
    if (target && type of target === 'object' && !Object.isFrozen(target)) {
      Object.freeze(target);
      Object.keys(targert).forEach(key => deepFreeze(target[key]));
    }
    return target;
}

 

 

 

 

 

출처

- 모던 자바스크립트 Deep Dive - 자바스크립트의 기본 개념과 동작 원리

728x90
반응형