16. 프로퍼티 어트리뷰트

heyhey·2023년 7월 20일
0
post-thumbnail

16.1 내부 슬롯과 내부 메서드

내부 슬롯과 내부 메서드는 JS 엔진의 구현 알고리즘을 설명하기 위한 의사 프로퍼티와 메서드이다.
이들은 실제로 동작은 하지만 외부로 공개된 객체는 아니다.
이중 대괄호로 감싼 이름들이 내부 슬롯과 메서드이다.

모든 객체는 [[Prototype]] 이라는 내부 슬롯을 갖는다.
직접 이 객체에 접근을 할 순 없지만, 이 객체는 __proto__를 통해 간접적 접근이 가능하다.

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

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

  • 프로퍼티의 상태는 아래의 상태를 뜻한다.
    • 프로퍼티 값
    • 값의 갱신 가능 여부
    • 열거 가능 여부
    • 재정의 가능 여부
const person = {
  name: "lee",
};

Object.getOwnPropertyDescriptor(person, "name");
// {value:'lee', writable:true, enumerable: true, configurable:true }
  • getOwnPropertyDescriptor 를 통해 프로퍼티 어트리뷰트를 조회할 수 있다.

    • 그 값으로 반환하는 값이 프로퍼티 디스크립터이다.
  • getOwnPropertyDescriptors 를 통해 전체 디스크립터 객체를 조회한다.

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

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

  • 데이터 프로퍼티
    • 키와 값으로 구성된 일반적 프로퍼티
  • 접근자 프로퍼티
    • 자체적 값이 아닌 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 호출하는 접근자 함수로 구성된 프로퍼티

데이터 프로퍼티

| 순서 => '프로퍼티 어트리뷰트' : '프로퍼티 디스크립터 객체의 프로퍼티'

  • [[Value]] : value

    • 프로퍼티 키를 통해 값에 접근하면 나오는 값
    • 프로퍼티 키를 통해 값을 변경하면 [[Value]]에 값을 재할당한다.
    • 프로퍼티가 없다면 동적 생성하고 생성된 프로퍼티의 [[Value]]에 값을 저장한다.
  • [[Writable]] : writable

    • 프로퍼티 값의 변경 가능 여부를 나타낸다.
    • false일 경우는 [[Value]]가 읽기 전용 프로퍼티가 된다
  • [[Enumerable]] : enumerable

    • 열거 가능 여부를 나타낸다.
    • for..in 이나 keys()를 사용할 수 있다.
  • [[Configurable]]: configurable

    • 재정의 가능 여부를 나타낸다.
    • false 일 경우 프로퍼티의 삭제 변경이 금지된다.
    • [[Writable]]이 true 일 경우,false로의 변경과, [[Value]]의 변경이 가능하다.
{
  value :'',
  writable:true,
  enumerable:true,
  configurable:true
}

기본값은 위와 같이 설정된다.

접근자 프로퍼티

접근자 프로퍼티는 자체적으로 값을 갖지 않고, 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티이다.

| 순서 => '프로퍼티 어트리뷰트' : '프로퍼티 디스크립터 객체의 프로퍼티'

  • [[Get]] : get

    • 접근자 프로퍼티를 통해 프로퍼티의 값을 읽을 때 호출되는 접근자이다.
    • 즉 접근자 프로퍼티 키로 프로퍼티 값에 접근하면 getter 함수가 호출되고 결과가 프로퍼티 값으로 반환된다.
  • [[Set]] : set

    • 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 합수이다.
    • 즉 접근자 프로퍼티 키로 프로퍼티 값을 저장하면 setter 함수가 호출되고 저장된다.
  • [[Enumerable]], [[Configurable]]

const person = {
  first_name: "홍", // 데이터 프로퍼티
  last_name: "길동",

  // 접근자 함수로 구성된 접근자 프로퍼티
  get fullName() {
    return `${this.first_name} ${this.last_name}`;
  },
  set fullName(name: string) {
    const names = name.split(" ");
    this.first_name = names[0];
    this.last_name = names[1];
  },
};
person.fullName = "김 철수";
person.fullName; // 참조를 하게 되면 getter 함수가 호출된다.

데이터 프로퍼티를 보면 이와같이 생겼다.
Object.getOwnPropertyDescriptor(person,'first_name')

{
  value :'홍',
  writable:true,
  enumerable:true,
  configurable:true
}

접근자 프로퍼티는 get,set,enurable,configurable 을 갖는다.

Object.getOwnPropertyDescriptor(person,'fullName')

{
  get:f,
  set:f,
  enumerable:true,
  configurable:true
}

접근자 프로퍼티는 자체적으로 값을 가지지 않으며 읽거나 저장할 때만 관여한다.

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

  1. 프로퍼티 키가 유효한지 확인한다.
  2. 프로토타입 체인에서 프로퍼티를 검색한다.
  3. 프로퍼티가 접근자인지 데이터 프로퍼티인지 확인한다.
  4. [[GET]]이나 [[Set ]]의 값을 반환한다. get,set

프로토타입

프로토 타입은 어떤 객체의 상위 객체의 역할을 하는 객체이다.
프로토타입은 하위 객체에게 자신의 프로퍼티와 메서드를 상속한다.
프로토타입 객체의 프로퍼티나 메서드를 상속받은 하위 객체는 프로퍼티를 자유롭게 사용 가능하다.

프로토타입 체인

프로토 타입이 단방향 linked list 형태로 연결되어 있는 상속 구조이다.
객체의 프로퍼티나 메서드에 접근하려고 할때, 해당 객체에 없다면 프로토타입 체인을 따라 차례대로 검색한다.

앞서 배운 내용을 직접 확인해보자
데이터 프로퍼티 prototype => {value,writable,enumerable:false,configurable:false}
접근자 프로퍼티 __proto__ => {get:f,set:f,enumerable:false,configurable:true}

16.4 프로퍼티 정의

프로퍼티를 추가하면서 프로퍼티 어트리뷰트(value,writable...)를 정의할 수 있다.

데이터 프로퍼티 정의

const person ={}
Object.defineProperty(person,'firstName'{
  value:'hi',
  writable:true,
  enumerable:true,
  configurable:true
})
Object.defineProperty(person,'lastName'{
  value:'hong',
})
Object .getOwnPropertyDescriptor(person, 'lastName' );
// {value: "hong", writable: false, enumerable: false, configurable: false}

객체의 프로퍼티를 누락시키면, false가 기본값이다. (값은 undefined)

enumerable: false

이 경우는 열거되지 않는다.
Object.keys(person) =>['firstName']

writable: false

person.lastName = 'DDONG'으로 변경하려고 하면, 에러는 발생하지 않지만 무시된다.

configurable: false

delete person.lastName 마찬가지로 재정의를 하려고 해도, 에러는 발생하지 않지만 무시되어 버린다.

접근자 프로퍼티 정의

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

16.5 객체 변경 방지

객체의 변경이 잦으면 좋지 않기 때문에 객체의 변경을 방지하는 메서드를 제공한다.
C(추가), R(값 읽기),U(값 쓰기),D(삭제), A(어트리뷰트 재정의)

  • 객체 확장 금지(RUDA) : Object.preventExtensions

    • person.age = 20; => 무시된다 (strict mode에서는 에러)
    • Object.defineProperty(person, 'age' , { value: 20 }); => 에러
  • 객체 밀봉(RU) : Object.seal

  • 객체 동결(R) : Object.freeze

    • 프로퍼티가 객체일 경우는 동결하지 못한다. 객체까지 동결시켜야 한다.
profile
주경야독

1개의 댓글

comment-user-thumbnail
2023년 7월 20일

항상 좋은 글 감사합니다.

답글 달기