프로퍼티 어트리뷰트

팀 러버덕 by Hoon

내부 슬롯과 내부 메서드

const myObject = {}

myObject.[[Prototype]] // Uncaught Syntax Error!

myObject.__proto__ // Object.prototype

내부 슬롯과 내부 메서드

프로퍼티 어트리뷰트 

 

  1. 프로퍼티의 값
  2. 갱신 가능 여부
  3. 열거 가능 여부
  4. 재정의 가능 여부

내부 슬롯과 내부 메서드

const person = {
  name: 'hoon'
}
// person 객체의 name 프로퍼티의 어트리뷰트 출력
console.log(Object.getOwnPropertyDescriptor(person, "name")) 
/*
  {
    configurable: true
    enumerable: true
    value: "hoon"
    writable: true
    [[Prototype]]: Object 
  }
*/

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

데이터 프로퍼티

 

  1. 키와 값으로 구성된 프로퍼티
  2. 우리가 살펴본 모든 프로퍼티들

접근자 프로퍼티

 

  1. 자체적으로 값이 없음
  2. 접근자 함수로 구성된 프로퍼티

데이터 프로퍼티

프로퍼티 어트리뷰트 프로퍼티 디스크립터 객체의 프로퍼티 설명
[[Value]] value - 프로퍼티 키를 통해 프로퍼티 값에 접근하면 반환되는 값
- 프로퍼티 키를 통해 값을 변경하면, [[Value]] 에 값을 재할당 프로퍼티가 존재하지 않으면 자동으로 생성한 뒤, 생성된 프로퍼티의 [[Value]] 에 값을 저장
​[[Writable]] writable - 프로퍼티 값의 변경 가능 여부를 표현 (불리언)
- [[Writable]] 값이 false 인 경우, [[Value]] 값을 변경할 수 없는 read-only 프로퍼티로 설정된다
​[[Enumerable]] enumerable - 프로퍼티 열거 가능 여부를 표현 (불리언)
- [[Enumerable]] 값이 false 인 경우, 해당 프로퍼티의 값은 열거할 수 없다 (for .. of / for .. in 사용 불가능)
​[[Configurable]] configurable - 프로퍼티의 재정의 가능 여부를 표현 (불리언)
- [[Configuratlbe]] 의 값이 false인 경우, 해당 프로퍼티 값 변경 또는 삭제가 금지된다
- [[Configurable]] 이 false여도, [[Value]] 변경과 [[Writable]] 값만 true로 설정할 수 있다

데이터 프로퍼티

const person = {
  name: 'hoon'
}
// person 객체의 name 프로퍼티의 어트리뷰트 출력
console.log(Object.getOwnPropertyDescriptor(person, "name")) 
/*
  {
    value: "hoon"
    writable: true
    enumerable: true
    configurable: true
    [[Prototype]]: Object 
  }
*/

접근자 프로퍼티

프로퍼티 어트리뷰트 프로퍼티 디스크립터 객체의 프로퍼티 설명
[[Get]] get - 데이터 프로퍼티의 값을 읽을 때 호출되는 접근자 함수
- 프로퍼티의 키로 접근하면, [[Get]] 의 값인 getter 함수가 호출되어 프로퍼티의 값을 반환
​[[Set]] set - 데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 함수
- 프로퍼티의 키로 값을 저장하면, [[Set]] 의 값인 setter 함수가 호출되어 프로퍼티의 값을 저장
​[[Enumerable]] enumerable - 프로퍼티 열거 가능 여부를 표현 (불리언)
- [[Enumerable]] 값이 false 인 경우, 해당 프로퍼티의 값은 열거할 수 없다 (for .. of / for .. in 사용 불가능)
​[[Configurable]] configurable - 프로퍼티의 재정의 가능 여부를 표현 (불리언)
- [[Configuratlbe]] 의 값이 false인 경우, 해당 프로퍼티 값 변경 또는 삭제가 금지된다
- [[Configurable]] 이 false여도, [[Value]] 변경과 [[Writable]] 값만 true로 설정할 수 있다
const person = {
  firstName: 'hoon',
  lastName: 'oh',

  // 프로퍼티 키 firstName과 lastName의 값을 받기위한 접근자 함수로 이루어진 접근자 함수
  // getter 함수
  get fullName() {
    return `${this.firstName} ${this.lastName}`
  },

  // 프로퍼티 키 firstName과 lastName의 값에 접근하기 위한 접근자 함수로 이루어진 접근자 함수
  // setter 함수
  set fullName(name) {
    [this.firstName, this.lastName] = name.split(' ')
  }
}

// 프로퍼티 키에 접근자 함수를 활용하여 프로퍼티의 값 참조
console.log(person.firstName + " " + person.lastName)

// 접근자 프로퍼티 setter를 활용해 프로퍼티 값에 저장
// setter 함수 호출
person.fullName = "John Cena"
console.log(person)

// 접근자 프로퍼티를 사용하여 프로퍼티 값 참조
// getter 함수 호출
console.log(person.fullName)

// firstName은 객체의 데이터 프로퍼티
// [[Value]] [[Writable]] [[Enumerable]] [[Configurable]]
let descriptor = Object.getOwnPropertyDescriptor(person, 'firstName')
console.log(descriptor);

// fullName은 객체의 접근자 프로퍼티
// [[Get]]] [[Set]] [[Enumerable]] [[Configurable]]
descriptor = Object.getOwnPropertyDescriptor(person, 'fullName')
console.log(descriptor);

접근자 프로퍼티

접근자 프로퍼티로 값에 접근하면

 

  1. [[Get]] 메서드 호출
  2. 프로퍼티 키가 유효한지 확인
  3. 프로토타입 체인에서 프로퍼티 검색
  4. 검색된 키의 프로퍼티가 데이터인지 접근자인지 확인
  5. 접근자 프로퍼티의 getter 함수를 호출하여 결과를 반환

프로퍼티 정의

const person = {};

Object.defineProperty(person, "firstName", {
  value: "Hoon",
  writable: true,
  enumerable: true,
  configurable: true,
});

const firstNameDescriptor = Object.getOwnPropertyDescriptor(
  person,
  "firstName"
);

console.log("firstName: ", firstNameDescriptor);
// firstName:  { 
//   value: 'Hoon', 
//   writable: true, 
//   enumerable: true, 
//   configurable: true 
// }

프로퍼티 정의

Object.defineProperty(person, "lastName", {
  value: "Oh",
});
const lastNameDescriptor = Object.getOwnPropertyDescriptor(person, "lastName");
console.log("lastName: ", lastNameDescriptor);
/**
   lastName:  {
    value: 'Oh',
    writable: false,
    enumerable: false,
    configurable: false
  } 
 */

console.log(Object.keys(person));
// [ 'firstName' ]

프로퍼티 정의

Object.defineProperty(person, "fullName", {
  get() {
    return `${this.firstName} ${this.lastName}`;
  },

  set(name) {
    [this.firstName, this.lastName] = name.split(" ");
  },
  enumerable: true,
  configurable: true,
});

const fullnameDescriptor = Object.getOwnPropertyDescriptor(person, "fullName");
console.log("fullName: ", fullnameDescriptor);
/**
fullName:  {
  get: [Function: get],
  set: [Function: set],
  enumerable: true,
  configurable: true
}
 */

객체 변경 방지

  1. 객체 확장 금지
  2. 객체 밀봉
  3. 객체 동결
  4. 불변 객체

객체 확장 금지

const person = {
  name: 'Hoon'
}
Object.preventExtensions(person)

console.log(Object.isExtensible(person)); // false

person.age = 31 // 무시 
console.log(person) // name: 'hoon'

Object.defineProperty(person, 'age', { value: 31}) // 무시
console.log(person) // name: 'hoon'

객체 밀봉

const person = {
  name: "Hoon",
};
Object.seal(person);

console.log(Object.isSealed(person)); // true

person.age = 31; // 무시
console.log(person); // name: 'hoon'

Object.defineProperty(person, "age", { value: 31 }); // 무시
console.log(person); // name: 'hoon'

delete person.name // 무시
console.log(person) // name: 'hoon' 

person.name = "Ellie"  // 재할당은 가능
console.log(person) // name: 'Ellie'

객체 동결

const person = {
  name: "Hoon",
};
Object.freeze(person);

console.log(Object.isFrozen(person)); // true

person.age = 31; // 무시
console.log(person); // name: 'hoon'

Object.defineProperty(person, "age", { value: 31 }); // 무시
console.log(person); // name: 'hoon'

delete person.name; // 무시
console.log(person); // name: 'hoon'

person.name = "Ellie"; // 무시
console.log(person); // name: 'hoon'

불변 객체

function deepFreeze(target) {
  // 타겟이 존재하고, 객체형 데이터이며, 동결이 되지 않았을 경우
  if (target && typeof target === 'object' && !Object.isFrozen(target)){
    // 현재 객체 동결
    Object.freeze(target)

    // 객체의 키 값으로 순환을 하여 각각의 키에 해당하는 요소 동결
    Object.keys(target).forEach(key => deepFreeze(target[key]))
  }
  return target
}
Made with Slides.com