Deep Dive 16장 프로퍼티 어트리뷰트

@hanminss·2021년 12월 1일
0

Deep Dive

목록 보기
8/16
post-thumbnail

16장 프로퍼티 어트리뷰트

1. 내부 슬롯과 내부 메서드

  • 내부슬롯과 내부 메서드는 자바스크립트 엔진의 구현 알고리즘을 설명하기 위해 ECMAScripts 사양에서 사용하는 의사 프로퍼티와 의사 메서드이다.
  • ECMAScript는 말 그대로 Ecma라는 기관이 만든 script 언어이며, ECMA-262 표준를 따르고 있다.
  • ES5, ES6, ES2020등은 ECMAScript가 배포된 버전이다.
  • 자바스크립트 엔진에서 실제로 동작하지만 개발자가 직접 접근할 수 있도록 공개된 객체 프로퍼티는 아니다.
  • 몇몇 프로퍼티는 간접적으로 접근할 수 있다.

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

  • 프로퍼티 어트리뷰트 : 프로퍼티의 상태를 나타낸다.
  • 프로퍼티의 상태: 프로퍼티의 값, 값의 갱신 가능 여부, 열거 가능 여부, 재정의 가능 여부
  • 프로퍼티를 생성할 때, 엔진은 프로퍼티 어트리뷰트를 기본값으로 자동 정의한다.
  • 프로퍼티 어트리뷰트는 내부 슬롯 [[Value]],[[Writable]],[[Enumerable]],,[[Configurable]] 이다.
  • 따라서 프로퍼티 어트리뷰트는 직접 접근할 수 없지만 메서드를 이용하여 간접적으로 확인할 수 있다.
const person = {
  name: "Lee",
};
console.log(Object.getOwnPropertyDescriptor(person, "name"));
//{ value: 'Lee', writable: true, enumerable: true, configurable: true }
  • Object.getOwnPropertyDescriptor 메서드는 어트리뷰트의 정보를 담아 반환하였다. 이를 프로퍼티 디스크립터 객체라 한다.
  • 존재하지 않거나 상속받은 프로퍼티는 undefined를 반환한다.
  • Object.getOwnPropertyDescriptors 메서드는 객체의 모든 프로퍼티의 프로퍼티 디스크립터 객체를 반환다.

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

  • 프로퍼티는 데이터 프로퍼티와 접근자 프로퍼티로 나뉜다.
  • 데이터 프로퍼티 : 키와 값으로 구성된 일반적인 프로퍼티.
  • 접근자 프로퍼티 : 자체적으로는 값을 갖지 않고 다른 데이터 프로퍼티 값을 읽거나 저장할 때 호출되는 접근자 함수로 구성된 프로퍼티

데이터 프로퍼티

  • 데이터 프로퍼티는 Value, Writable, Enumerable, Conmfigurable 네가지의 프로퍼티 어트리뷰트를 갖는다.
  • Value : 프로퍼티 키를 통해 프로퍼티 값에 접근하면 반환되는 값
  • Writable : 프로퍼티 값의 변경 가능 여부를 나타내며 불리언 값이다.
  • Enumerable : 프로퍼티의 열거 가능 여부를 나타내며 불리언 값이다. false 이면 for in 이나 object.keys 등으로 열거될 수 없다.
  • Configurable : 재정의 가능여부를 나타내며 불리언 값이다. false 이면 프로퍼티의 삭제, 값의 변경이 금지된다. 단, writable이 true 이면 value의 변경과 writable을 false로 바꾸는 것은 허용
  • writable, enumerable, configurable 세가지는 default 값이 true이다.

접근자 프로퍼티

  • 접근자 프로퍼티는 자체적으로 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티다.
  • 접근자 프로퍼티는 Get, Set, Enumerable, Configurable 네가지의 프로퍼티 어트리뷰트를 갖는다.
  • Get : 접근자 프로퍼티를 통해 데이터 프로퍼티 값을 읽을 때 호출되는 접근자 함수다.
  • Set : 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 함수다.
  • 접근자 함수는 getter/setter 함수라고 불린다.
const person = {
  firstName: "hanmin",
  lastName: "Yim",
  get fullName() {
    // getter 함수
    return `${this.firstName} ${this.lastName}`;
  },
  set fullName(name) {
    // setter 함수
    [this.firstName, this.lastName] = name.split(" ");
  },
};
// fullName 이 접근자 프로퍼티가 된다.

// 데이터 프로퍼티를 통한 프로퍼티 잠조
console.log(person.firstName + " " + person.lastName); // hanmin Yim
// 접근자 프로퍼티를 통한 프로퍼티 참조
console.log(person.fullName); // hanmin Yim

// 접근자 프로퍼티를 통한 값의 저장
person.fullName = "hanmin ss";
console.log(person.fullName); // hanmin ss

console.log(Object.getOwnPropertyDescriptor(person, "firstName"));
//  {
//     value: 'hanmin',
//     writable: true,
//     enumerable: true,
//     configurable: true
//   }
console.log(Object.getOwnPropertyDescriptor(person, "fullName"));
// {
//     get: [Function: get fullName],
//     set: [Function: set fullName],
//     enumerable: true,
//     configurable: true
// }
  • fullName 프로퍼티가 접근자 프로퍼티가 되고 getter와 setter를 같은 식별자로 선언해 주어야 한다.

4. 프로퍼티 정의

  • 프로퍼티 정의: 새로운 프로퍼티를 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의하거나 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의 하는 것
  • Object.defineProperty 메서드를 사용하여 프로퍼티의 어트리뷰트를 정의할 수 있다.
const person = {};

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

Object.defineProperty(person, "lastName", {
  value: "Yim",
});

let propertyAtt = Object.getOwnPropertyDescriptors(person);

console.log(propertyAtt);
// {
//     firstName: {
//       value: 'hanmin',
//       writable: true,
//       enumerable: true,
//       configurable: true
//     },
//     lastName: { <- 디스크립터 객체의 프로퍼티를 누락시키면 default는 false, undefined 가 된다.
//       value: 'Yim',
//       writable: false,
//       enumerable: false,
//       configurable: false
//     }
//   }

console.log(Object.keys(person));
//[ 'firstName' ] lastName 의 enumerable이 flase 이기 때문에 열거되지 않았다.

person.lastName = "kim";
// lastName의 writable이 false이기 때문에 반영되지 않는다, 이때 오류가 나지 않고 무시된다.

delete person.lastName;
// lastName의 configurable이  false 이기 때문에 삭제되지 않는다. 이때 오류가 나지 않고 무시된다.

// Object.defineProperty(person, "lastName", {
//   enumerable: true, //TypeError: Cannot redefine property: lastName : configurable 이 false이기 때문에 witable과 configurable 이외에 아무것도 바꿀 수 없다.

// });

// 접근자 프로퍼티

Object.defineProperty(person, "fullName", {
  get() {
    return `${this.firstName} ${this.lastName}`;
  },
  set(name) {
    [this.firstName, this.lastName] = name.split(" ");
  },
  enumerable: true,
  configurable: true,
});

console.log(person.fullName); // hanmin Yim

person.fullName = "한민 임";

console.log(person.fullName); // 한민 Yim : lastName의 configurable 이 false이기 때문 ..
  • Object.defineProperties 를 이용하면 한번에 여러가지 프로퍼티를 정의할 수 있다.
const person = {};

Object.defineProperties(person, {
  firstName: {
    value: "hanmin",
    writable: true,
    enumerable: true,
    configurable: true,
  },
  lastName: {
    value: "Yim",
    writable: true,
    enumerable: true,
    configurable: true,
  },

  fullName: {
    get() {
      return `${this.firstName} ${this.lastName}`;
    },

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

5. 객체 변경 방지

  • 자바스크립트는 객체 변경을 방지하는 다양한 메서드를 제공한다.
    구분 메서드 pp 추가 pp 삭제 pp 값 읽기 pp 값 쓰기 pp 어트리뷰트 재정의
    객체 확장 금지 Object.preventExtensions X O O O O
    객체 밀봉 Object.seal X X O O X
    객체 동결 Object.freeze X X O X X

객체 확장 금지

  • 객체 확장 금지란, 객체의 프로퍼티를 추가하는 것을 금지하는 것이다.
  • 프로퍼티 동적추가 방법과 object.defineProperty 방법이 모두 금지된다.
  • Object.preventExtensions(obj)를 이용하여 객체 확장금지를 한다.
  • 확장 가능한 객체인지는 Object.isExtensible 메서드로 확인할 수 있다.
// 객체 확장 금지

const dog = {
  name: "cream",
};

console.log(Object.isExtensible(dog));
// true

Object.preventExtensions(dog);

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

객체 밀봉

  • 객체 밀봉은 프로퍼티 추가 및 삭제, 프로퍼티 어트리뷰트 재정의를 금지한다.
  • 즉, 밀봉된 개체는 읽기와 쓰기만 가능하다.
  • Object.seal(obj)를 이용하여 객체 밀봉을 한다.
  • Object.isSealed 함수로 밀봉되었는지 확인할 수 있다.
// 객체 밀봉 : 프로퍼티 추가, 삭제, 재정의 금지

const dog = {
  name: "cream",
};

console.log(Object.isSealed(dog));
// false

Object.seal(dog);

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

console.log(Object.getOwnPropertyDescriptors(dog));
// configurable : false 로 바뀐다.

객체 동결

  • 객체 동결은 프로퍼티 추가, 삭제, 프로퍼티 어트리뷰트 재정의금지, 값 갱신 금지를 의미한다.
  • 즉, 읽기만 가능하다.
  • Object.freeze를 이용하여 객체를 동결시킨다.
  • 동결된 객체는 Object.isFrozen으로 확인할 수 있다.
// 객체 동결 : 프로퍼티 추가, 삭제, 값 갱신, 프로퍼티 어트리뷰트 재정의를 모두 금지한다. 읽기만 가능

const dog = {
  name: "cream",
};

console.log(Object.isFrozen(dog));
// false

Object.freeze(dog);

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

console.log(Object.getOwnPropertyDescriptors(dog));
// configurable과 writable이 false 로 바뀐다.

불변 객체

  • 동결까지가면 완벽한것 같지만 사실 중첩 객체에는 영향을 주지 못한다.
  • 따라서 재귀함수를 구현하여 중첩 객체까지 변경이 불가능한 읽기 전용의 불변 객체를 만들 수 있다.
const dog = {
  name: "cream",
  age: {
    age: 30,
  },
};

Object.freeze(dog);
console.log(Object.isFrozen(dog)); // true

console.log(Object.isFrozen(dog.age)); // false
// 불변객체를 만들기 위한 재귀함수
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 dog = {
  name: "cream",
  age: {
    age: 30,
  },
};

deepFreeze(dog);
console.log(Object.isFrozen(dog)); // true

console.log(Object.isFrozen(dog.age)); // true

0개의 댓글