16 프로퍼티 어트리뷰트

이재철·2021년 9월 13일
0

javaScript

목록 보기
8/19
post-thumbnail

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

  • 자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰트를 기본값으로 자동 정의

  • 프로퍼티 상태 : 프로퍼티 값(value), 값의 갱신 가능 여부(writable), 열거 가능 여부(enumerable), 재정의 가능 여부(configurable)

const person = {
   name: 'Lee'
};

person.age = 20;
// 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크럽터 객체 반환
console.log(Object.getOwnPropertyDescriptor(person, 'name'));
// name : {value: "Lee", writable: true, enumerable: true, configurable: true}
// age : {value: 20, writable: true, enumerable: true, configurable: true}

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

  • 데이터 프로퍼티(data property) : 키와 값으로 구성된 일반적인 프로퍼티

데이터 프로퍼티

프로퍼티 디스크립터 객체의 프로퍼티

  • value
    • 프로퍼티 키를 통해 프로퍼티 값에 접근하면 반환되는 값
    • 프로퍼티가 없으면 프로퍼티를 동적 생성하고 생성된 프로퍼티의 [[Value]]에 값을 저장
  • writable
    • 프로퍼티 값의 변경 가능 여부를 나타내며 불리언 값을 갖음
    • false 경우 해당 프로퍼티의 [[Value]]의 값을 변경할 수 없는 읽기 전용 프로퍼티
  • enumerable
    • 프로퍼티의 열거 가능 여부를 나타내는 불리언 값을 갖음
    • false 경우 해당 프로퍼티는 for..in 문이나 Object.keys 메서드 등으로 열거할 수 없음
  • configurable
    • 프로퍼티의 재정의 가능 여부를 나타내머 불리언 값을 갖음
    • false 경우 프로퍼티의 삭제, 프로퍼티 어트리뷰트 값의 변경이 금지
    • true인 경우 [[Value]]의 변경과 [[Writable]]을 false로 변경 허용

접근자 프로퍼티

  • 접근자 프로퍼티(accessor property) : 자체적으로는 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 호출되는 접근자 함수(accessor function)로 구성된 프로퍼티

프로퍼티 디스크립터 객체의 프로퍼티

  • get
    • 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 읽을 때 호출되는 접근자 함수
  • Set
    • 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 함수
  • enumerable
    • 데이터 프로퍼티의 [[enumerable]]과 같음
  • configurable
    • 데이터 프로퍼티의 [[configurable]]과 같음
const person = {
 // 데이터 프로퍼티
 fristName: 'Ungmo',
 laseName: 'Lee',
  
 // fullName은 접근자 함수로 구성된 접근자 프로퍼티
 // getter 함수
 get fullName() {
   return `${this.firstName} ${this.lastName}`;
 },
  
 //setter 함수
 set fullName(name) {
  [this.firstName, this.lastName] = name.split(' ');
 }
};

// 데이터 프로퍼티를 통한 프로퍼티 값 참조
console.log(person.firstName + ' ' + person.lastName); // Ungmo Lee

// 접근자 프로퍼티 funllName에 값을 저장하면 setter 함수가 호출
person.fullName = 'Heegun Lee';
console.log(person); // {firstName: "Heegun", lastName: "Lee"}

💡 프로토타입 (prototype)

  • 어떤 객체의 상위(부모) 객체의 역활을 하는 객체
  • 프로토타입은 하위(자식) 객체에게 자신의 프로퍼티와 메서드를 상속함
// 일반 객체의 __proto__는 접근자 프로퍼티

Object.getOwnPropertyDescriptor(Object.prototype, '__proto__');

// 함수 객체의 prototype은 데이터 프로퍼티
Object.getOwnPropertyDescriptor(function() {}, 'prototype');

프로퍼티 정의

  • 새로운 프로퍼티를 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의하거나, 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의하는 것

  • Object.defineProperty 메서드를 통해 프로퍼티의 어트리뷰트를 정의할 수 있음

  • Object.defineProperty 메서드로 프로퍼티를 정의할 때 프로퍼티 디스크립터 객체의 프로퍼티를 일부 생략 가능

    • value - [[value]] - 기본값(undefined)
    • get - [[get]] - 기본값(undefined)
    • set - [[set]] - 기본값(undefined)
    • writable - [[writable]] - 기본값(false)
    • enumerable - [[enumerable]] - 기본값(false)
    • configurable - [[configurable]] - 기본값(false)
const person ={};

Object.defineProperties(person,{
  //데이터 프로퍼티
  firstName:{
    value : 'Jayce',
    writable : true,
    enumerable : true,
    configurable : true
  },
  lastName:{
    value : 'Yu',
    writable : true,
    enumerable : true,
    configurable : true
  },
  
  //접근자 프로퍼티
  fullName : {
    get(){
      return `$[this.firstName] $[this.lastName]`;
    };
    
    set(name){
  		[this.firstName, this.lastName] = name.split(' ');
	},
    enumerable : true,
    configurable : true
 }
});

객체 변경 방지

  • 객체는 변경 가능한 값이므로 재할당 없이 직접 변경 가능
  • 프로퍼티 추가, 삭제, 갱신, 프로퍼티 어트리뷰트를 재정의

객체 확장 금지

  • Object.preventExtensions 메서드
    • 프로퍼티 추가 불가능
    • 프로퍼티 삭제, 읽기, 값 쓰기, 어트리뷰트 재정의 가능
    • Object.isExtensible 메서드로 객체 확장 체크 가능
const person = { name: 'Lee' };

console.log(Object.isExtensible(person)); // true
Object.preventExtensions(person); // 프로퍼티 추가 금지
console.log(Object.isExtensible(person)); // false

person.age = 20; // 무시 strict mode에서는 에러
console.log(person); // {name: "Lee"}

delete person.name;
console.log(person); // {}
// 프로퍼티 정의에 의한 프로퍼티 추가도 금지
Object.defineProperty(person, 'age', { value: 20 }); // TypeError

객체 밀봉

  • Object.seal 메서드
    • 밀봉된 객체는 읽기와 쓰기만 가능
    • Object.isSealed 메서드로 밀봉 객체 체크 가능
const person = { name: 'Lee' };

console.log(Object.isSealed(person)); // false
Object.seal(person); // 객체 밀봉하여 프로퍼티 추가, 삭제, 재정의 금지
console.log(Object.isSealed(person)); // true

// 밀봉된 객체는 configurable -> false
console.log(Object.getOwnPropertyDescriptors(person));
// name: {value: "Lee", writable: true, enumerable: true, configurable: false}

person.age = 20; // 무시 strict mode에서는 에러
console.log(person); // {name: "Lee"}

delete person.name; // 무시 strict mode에서는 에러
console.log(person); // {name: "Lee"}
// 프로퍼티 정의에 의한 프로퍼티 추가도 금지
Object.defineProperty(person, 'age', { value: 20 }); // TypeError

객체 동결

  • Object.freeze 메서드
    • 동결된 객체는 읽기만 가능
    • Object.isFrozen 메서드로 동결 객체 체크 가능
const person = { name: 'Lee' };

console.log(Object.isFrozen(person)); // false
Object.freeze(person); // 객체 동결 프로퍼티 추가, 삭제, 재정의, 쓰기 금지
console.log(Object.isFrozen(person)); // true

// 밀봉된 객체는 configurable -> false
console.log(Object.getOwnPropertyDescriptors(person));
// name: {value: "Lee", writable: true, enumerable: true, configurable: false}

person.age = 20; // 무시 strict mode에서는 에러
console.log(person); // {name: "Lee"}

delete person.name; // 무시 strict mode에서는 에러
console.log(person); // {name: "Lee"}

person.name = 'Kim'; // 무시 strict mode에서는 에러
console.log(person); // {name: "Lee"}

// 프로퍼티 정의에 의한 프로퍼티 추가도 금지
Object.defineProperty(person, 'name', { configuralbe: true }); // TypeError

불변 객체

  • Object.freeze 메서드로 객체를 동결해도 중첩 객체까지 동결 불가능
const person = {
  name: 'Lee',
  address: { city: 'Seoul' }
};

// 얕은 객체 동결
Object.freeze(person);

// 직속 프로퍼티만 동결
console.log(Object.isFrozen(person)); // true
// 중첩 객체까지 동결 불가능
console.log(Object.isFrozen(person.address)); // false

person.address.city = 'Busan';
console.log(person); // {name: "Lee", address: {city: "Busan"}}

객체 중첩 객체까지 동결하여 변경이 불가능한 읽기 전용의 불변 객체 구현

  function deepFreeze(target) {
   if (target && typeof target === 'object' && !Object.isFrozen(target)) {
     Object.freeze(target);
     Object.keys(target).forEach(key => deepFreeze(target[key]));
   }
   return target;
 }

const person = {
  name: 'Lee',
  address: { city: 'Seoul' }
};

// 얕은 객체 동결
deepFreeze(person);

// 직속 프로퍼티만 동결
console.log(Object.isFrozen(person)); // true
// 중첩 객체까지 동결
console.log(Object.isFrozen(person.address)); // true

person.address.city = 'Busan';
console.log(person); // {name: "Lee", address: {city: "Seoul"}}


📖 참고도서 : 모던 자바스크립트 Deep Dive 자바스크립트의 기본 개념과 동작 원리 / 이웅모 저 | 위키북스

profile
혼신의 힘을 다하다 🤷‍♂️

0개의 댓글