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

조재권·2021년 6월 24일
0
post-thumbnail

16장 프로퍼티 어트리뷰트


출처 모던 자바스크립트 Deep Dive을 보고 정리한 내용입니다.

내부 슬롯과 내부 메서드

  • 내부 슬롯과 내부 메서드는 자바스크립트 엔진의 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사용하는 의사 프로퍼티(psuedo property), 의사 메서드(psuedo method)이다.
  • 이중 대괄호([[...]])를 사용하여 표현한다.
  • 직적 접근 및 호출이 불가능하지만 프로토타입은 obj.__prototype__ 을 통해 간접적으로 사용가능하다.

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

프로퍼티 어트리뷰트란 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내기 위해 자바스크립트 엔진이 생성하는 값이다.

const obj = {
 name: 'JKCho'
};

console.log(Object.getOwnPropertyDescriptor(obj, 'name'));
//{value: "JKCho", writable: true, enumerable: true, configurable: true}

obj.age = 26;

console.log(Object.getOwnPropertyDescriptors(obj));
//{
// name: {value: "JKCho", writable: true, enumerable: true, configurable: true},
// age: {value: 26, writable: true, enumerable: true, configurable: true}
//}

Object.getOwnPropertyDescriptor 함수를 통해 접근할 수 있으며 value, writable, enumerable, configurable를 가진 프로퍼티 디스크럽터 객체가 반환된다. value는 값은 객체의 value값과 동일하고, 나머지 값은 기본적으로 true로 설정되어 있다.

Object.getOwnPropertyDescriptors함수는 객체의 모든 프로퍼티의 프로퍼티 디스크럽터를 반환한다.

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

  • 데이터 프로퍼티 : key와 value로 구성된 일반적인 프로퍼티
  • 접근자 프로퍼티 : 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 호출되는 접근자 함수로 구성된 프로퍼티

데이터 프로퍼티

const obj = {};

//프로퍼티 어트리뷰트 정의
Object.defineProperty(obj, 'firstName', {
  value: 'JK',
  writable: true,
  enumerable: true,
  configurable: true
});

console.log(Object.getOwnPropertyDescriptor(obj, 'firstName'));
//{value: "JK", writable: true, enumerable: true, configurable: true}

Object.defineProperty(obj, 'lastName', {
  value: 'Cho',
});

console.log(Object.getOwnPropertyDescriptor(obj, 'lastName'));
//{value: "Cho", writable: false, enumerable: false, configurable: false}
//프로퍼티를 누락시키면 undefined 또는 false로 할당된다.

obj.lastName = 'Kim' //writable이 false인 경우 동작하지 않는다. 에러는 발생하지 않고 무시된다.

console.log(Object.keys(obj)); //['firstName']
//enumerable이 false인 경우 for...in문과 함께 열거되지 않는다

delete obj.lastName; //configurable이 false인 경우 무시된다. writable이 true인 경우 value를 변경하거나 writable의 값을 false로 변경하는 것은 가능하다.

접근자 프로퍼티

const obj = {};

Object.defineProperties(obj, {
  firstName: {
    value: 'JK',
  	writable: true,
  	enumerable: true,
	  configurable: true
  },
  
  lastName: {
    value: 'Cho',
  	writable: true,
  	enumerable: true,
	  configurable: true
  },
  
  fullName: {
    get() { //getter 함수
      return`${this.firstName, this.lastNAme}`;
    }, 
    set(name) { //setter함수
      [this.firstName, this.lastName] = name.split(' ');
		}
  }
});

const descriptor = Object.getOwnPropertyDescriptor(obj, 'fullName');
console.log(descriptor); //fullName {get: f, set: f, enumerable: true, configurable: true}

obj.fullName('Peter Park');
console.log(obj); //firstName: 'Peter', lastName: 'Park'

객체 변경 방지

  • Object.preventExtensions: 객체의 확장을 금지한다. 확장이 금지된 객체는 프로퍼티 추가할 수 없다.
  const obj = {name: 'JKCho'};
  
  console.log(obj.isExtensible); //true
  
  Object.preventExtensions(true);
  console.log(obj.isExtensible); //false
  
  obj.age = 26; //무시, strict모드에서는 에러 발생
  
  delete obj.name;
  console.log(obj); //{}, 삭제는 가능
  • Object.seal: 객체를 밀봉하여 읽고, 쓰기만 가능하게 한다.
  const obj = {name: 'JKCho'};
  
  console.log(obj.isSealed); //false
  
  Object.seal(true);
  console.log(obj.isSealed); //true
  
  console.log(Object.getOwnPropertyDescriptors(obj));
  //{ name: {value: 'JKCho', writable: true, enumerable: true, configurable: false},}
  
  obj.age = 26; //무시, strict모드에서는 에러 발생
  
  delete obj.name; //무시, strict모드에서는 에러 발생
  console.log(obj); //{name: 'JKCho'}
  
  obj.name = 'PeterKim';
  console.log(obj); //{name: 'PeterKim'}, 프로퍼티 값 갱신은 가능
  
  Object.defineProperty(obj, 'name', {configurable: true});//TypeError, 프로퍼티 어트리뷰트는 재정의가 금지된다.
  • Object.freeze: 객체를 동결하여 객체를 읽을 수만 있게 한다.
  const obj = {name: 'JKCho'};
  
  console.log(obj.isFrozen); //false
  
  Object.freeze(true);
  console.log(obj.isFrozen); //true
  
  console.log(Object.getOwnPropertyDescriptors(obj));
  //{ name: {value: 'JKCho', writable: false, enumerable: true, configurable: false},}
  
  obj.age = 26; //무시, strict모드에서는 에러 발생
  
  delete obj.name; //무시, strict모드에서는 에러 발생
  console.log(obj); //{name: 'JKCho'}
  
  obj.name = 'PeterKim';
  console.log(obj); //{name: 'JK Cho'}, 무시, 프로퍼티 값 갱신이 불가능하다
  
  Object.defineProperty(obj, 'name', {configurable: true});//TypeError, 프로퍼티 어트리뷰트는 재정의가 금지된다.

불변 객체

위와 같은 방식의 변경 방지는 얕은 변경 방지로 직속 프로퍼티만 변경되고 중첩 객체의 프로퍼티에는 적용되지 않는다.

const obj = {
  name: 'JKCho'
	location: {
  	city: '경기도',
  	state: '안양시'
	}
};

Object.freeze(obj);
console.log(Object.isFrozen(obj)); //true
console.log(Object.isFrozen(obj.location)); //false

그러므로 다음과 같이 재귀함수를 이용해 중첩객체까지 변경방지를 적용할 수 있다.

const obj = {
  name: 'JKCho'
	location: {
  	city: '경기도',
  	state: '안양시'
	}
};

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

deepFreeze(obj);

console.log(Object.isFrozen(obj)); //true
console.log(Object.isFrozen(obj.location)); //true
profile
프론트엔드 새내기의 발전 일기

0개의 댓글