[Deep dive] 16. 프로퍼티 어트리뷰트

정호·2023년 4월 23일
0

내부슬롯과 내부 메서드

내부 슬롯과 내부 메서드는 자바스크립트 엔진의 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사용하는 의사 프로퍼티의사 메서드 이다.
[[...]] 이중 대괄호로 감싼 이름들

실제로 개발자가 접근할 수는 없다. 단 일부 내부 슬롯, 내부 메서드에 한해 간접적으로 접근할 수 있는 수단을 제공한다.

ex) 모든 객체는 [[Prototype]]이라는 내부 슬롯을 가진다. 직접접근이 불가하므로 __proto__로 접근한다.

Prototype에 대한 블로깅

const o = {};

// 내부 슬롯은 자바스크립트 엔진의 내부 로직이므로 직접 접근할 수 없다.
o.[[Prototype]] // -> Uncaught SyntaxError: Unexpected token '['
// 단, 일부 내부 슬롯과 내부 메서드에 한하여 간접적으로 접근할 수 있는 수단을 제공하기는 한다.
o.__proto__ // -> Object.prototype

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

자바스크립트 엔진은 프로퍼티를 생성할때 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰트를 자동 정의한다.

내부 상태 값인 내부 슬롯
[[Value]],[[Writable]],[[Enumerable]],[[Configurable]]
Object.getOwnPropertyDescriptor메서드로 간접적으로 확인은 가능

const person = {
  name: 'Lee'
};

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

매개변수: 객체의 참조전달, 프로퍼티 키를 문자열로 전달
Object.getOwnPropertyDescriptor메서드는 프로퍼티 디스크립터 객체를 반환한다.

const person = {
  name: 'Lee'
};

// 프로퍼티 동적 생성
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}
}
*/

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

프로퍼티는 데이터 프로퍼티와 접근자 프로퍼티로 구분한다.

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

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

getOwnPropertyDescriptor가 반환한 디스크립터 객체

  • value프로퍼티의 값은 'Lee'
  • writable enumerable configurable 프로퍼티의 값은 true
    - 이는 [[Writable]] [[Enumerable]] [[Configurable]]의 값이 true인것을 뜻한다.
const person = {
  name: 'Lee'
};

// 프로퍼티 동적 생성
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}
}
*/

이처럼 프로퍼티가 생성될때 [[Value]]의 값은 프로퍼티 값으로 초기화 되며 [[Writable]] [[Enumerable]] [[Configurable]]의 값이 true로 초기화된다.

접근자 프로퍼티

getter/setter 함수

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

  // fullName은 접근자 함수로 구성된 접근자 프로퍼티다.
  // getter 함수
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  },
  // setter 함수
  set fullName(name) {
    // 배열 디스트럭처링 할당: "31.1 배열 디스트럭처링 할당" 참고
    [this.firstName, this.lastName] = name.split(' ');
  }
};

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

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

// 접근자 프로퍼티를 통한 프로퍼티 값의 참조
// 접근자 프로퍼티 fullName에 접근하면 getter 함수가 호출된다.
console.log(person.fullName); // Heegun Lee

// firstName은 데이터 프로퍼티다.
// 데이터 프로퍼티는 [[Value]], [[Writable]], [[Enumerable]], [[Configurable]] 프로퍼티 어트리뷰트를 갖는다.
let descriptor = Object.getOwnPropertyDescriptor(person, 'firstName');
console.log(descriptor);
// {value: "Heegun", writable: true, enumerable: true, configurable: true}

// fullName은 접근자 프로퍼티다.
// 접근자 프로퍼티는 [[Get]], [[Set]], [[Enumerable]], [[Configurable]] 프로퍼티 어트리뷰트를 갖는다.
descriptor = Object.getOwnPropertyDescriptor(person, 'fullName');
console.log(descriptor);
// {get: ƒ, set: ƒ, enumerable: true, configurable: true}
  • person 객체의 firstName, lastName프로퍼티는 일반적인 데이터 프로퍼티
  • getter/setter함수의 이름 fullName이 접근자 프로퍼티
    -> 자체적으로 값을 갖지 않고 데이터 프로퍼티의 값을 읽거나 저장에 관여

프로퍼티 정의

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

Object.defineProperty메서드

const person = {};

// 데이터 프로퍼티 정의
Object.defineProperty(person, 'firstName', {
  value: 'Ungmo',
  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
});

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

person.fullName = 'Heegun Lee';
console.log(person); // {firstName: "Heegun", lastName: "Lee"}

객체 변경 방지

객체는 변경 가능한 값이므로 재할당 없이 직접 변경할 수 있다.

객체 확장 금지

Object.preventExtensions메서드는 객체의 확장을 금지한다. -> 프로퍼티 추가가 금지된다.

확장이 가능한지 여부는 Object.isExtensible메서드로 확인

객체 밀봉

Object.seal메서드는 객체를 밀봉한다.
-> 프로퍼티 추가 및 삭제와 프로퍼티 어트리뷰트 재정의를 금지한다.
--> 밀봉된 객체는 읽기와 쓰기만 가능하다.

객체 동결

Object.freeze메서드는 객체를 동결한다.
프로퍼티 추가 및 삭제와 프로퍼티 어트리뷰트 재정의 금지, 프로퍼티 값 갱신 금지를 의미한다.
-> 동결된 객체는 읽기만 가능하다.

불변 객체

profile
열심히 기록할 예정🙃

0개의 댓글