[딥다이브] 16장 | 프로퍼티 어트리뷰트

내부 슬롯과 내부 메서드

  • 의사 프로퍼티와 의사 메서드

  • 이중 대솰호 ([[...]])

  • 자바스크립트 엔진에서 실제로 동작하지만 개발자가 접근, 호출 불가

    • 자바스크립트 엔진 내부 로직임
    • 하지만 일부 내부 슬롯과 내부 메서드는 간접적으로 접근할 수 있는 수단 제공
  • 모든 객체는 [[Prototype]] 내부 슬롯 가짐

    • proto를 통해 간접적 접근 가능

    • const student = {};
      student.[[Prototype]] // SyntaxError
      student.__proto__ // Object.prototype

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

  • 프로퍼티 어트리뷰트: 프로퍼티의 상태를 나타냄

    • 프로퍼티의 상태: 프로퍼티의 값, 값의 갱신 가능 여부, 열거 가능 여부, 재정의 가능 여부
    • 자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티 어트리뷰트를 기본값으로 자동 정의함
    • 자바스크립트 엔진이 관리하는 내부 상태 값인 내부 슬롯
      • [[Value]], [[Writable]], [[Enumerable]], [[Configurable]]
  • Object.getOwnPropertyDescriptor 메서드로 간접 확인 가능

    const student = {
    	name: 'Dory'
    }
    
    console.log(Object.getOwnPropertyDescriptor(student, 'name')) // {value: "Dory", writable: true, enumerable: true, configurable: true}
    • 첫 번째 매개변수: 객체의 참조, 두번째 매개변수: 프로퍼티 키
    • 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터 객체 반환
      • 존재하지 않는 프로퍼티 || 상속받은 프로퍼티에 대해선 undefined
    • 하나의 프로퍼티에 대해 디스크립터 객체 반환
      • ES8의 Object.getOwnPropertyDescriptors는 모든 프로퍼티의 프로퍼티 어트리뷰트 제공하는 프로퍼티 디스크립터 객체들 반환

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

  • 데이터 프로퍼티 : 키와 값으로 구성된 일반적인 프로퍼티
  • 접근자 프로퍼티 : 자체적으로 값을 갖진 X, 다른 데이터 프로퍼티의 값을 읽고 저장할 때 호출되는 접근자 함수로 구성됨

데이터 프로퍼티

  • 데이터 프로퍼티의 프로퍼티 어트리뷰트 : 자바스크립트 엔진이 프로퍼티 생성할 때 기본값으로 자동 정의됨
    • [[Value]]는 프로퍼티 값, 나머지는 true로 초기화됨
    • 프로퍼티 동적 추가해도 위와 마찬가지
프로퍼티 어트리뷰트프로퍼티 디스크립터
객체의 프로퍼티
설명
[[Value]]value프로퍼티 키를 통해 반환되는 값
프로퍼티 키를 통해 값을 변경하면 [[Value]]에 값을 재할당함
프로퍼티가 없으면 프로퍼티를 동적 생성 & 생성된 프로퍼티의 [[Value]]에 값 저장
[[Writable]]writable프로퍼티 값의 변경 여부, 불리언 값
false인 경우 프로퍼티의 [[Value]]의 값은 읽기 전용 프로퍼티가 됨
[[Enumerable]]enumerable열거 가능 여부, 불리언 값
false인 경우 for...in, Object.keys 메서드 등으로 열거 불가
[[Configurable]]configurable재정의 가능 여부, 불리언 값
false인 경우 삭제, 값 변경이 금지됨
하지만 [[Writable]]이 true인 경우 [[Value]] 변경과 [[Writable]]을 false로 변경하는 것은 허용됨

접근자 프로퍼티

  • 자체적으로 값 X, 다른 데이터 프로퍼티의 값 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티
프로퍼티 어트리뷰트프로퍼티 디스크립터
객체의 프로퍼티
설명
[[Get]]get데이터 프로퍼티 값 읽을 때 호출되는 접근자 함수
접근자 프로퍼티 키로 프로퍼티 값에 접근하면 [[Get]]의 값, getter 함수가 호출되고 결과가 프로퍼티 값으로 반환됨
[[Set]]set데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 함수
접근자 프로퍼티로 값을 저장하면 [[Set]]의 값, setter 함수가 호출되고 그 결과가 프로퍼티 값으로 저장됨
[[Enumerable]]enumerable데이터 프로퍼티의 [[Enumerable]]과 같음
[[Configurable]]configurable데이터 프로퍼티의 [[Configurable]]과 같음
  • 접근자 함수 = getter/setter 함수

    • 접근자 프로퍼티는 getter와 setter 모두 정의할 수도 있고 하나만 정의할 수도 있음

    • const student = {
      	month: '3'; // month, day는 데이터 프로퍼티
        day: '13';
        get fullBirthDay() {
          return `${this.month} ${this.day}`;
        },
        set fullBirthDay() {
          [this.month, this.day] = name.split('');
        }
      }
      
      // 접근자 프로퍼티를 통해 프로퍼티 값 저장
      // setter 함수가 호출됨
      student.fullBirthDay = '2 15';
      // 접근자 프로퍼티를 통해 프로퍼티 값 참조
      // getter 함수가 호출됨
      console.log(student.fullBirthDay); // 2 15
      
      // fullBirthDay는 접근자 프로퍼티
      // get, set, enumerable, configurable을 갖는다
      // 프로퍼티 어트리뷰트를 갖는다
      descriptor = Object.getOwnPropertyDescriptor(student, 'fullBirthDay');
      console.log(descriptor); // {get: f, set: f, enumerable: true, configurable: true}
    • 메서드 앞에 get,set이 붙은 것 → getter, setter 함수

      • 함수의 이름 fullBirthDay가 접근자 프로퍼티임
      • 접근자 프로퍼티는 데이터 프로퍼티 값을 읽기/저장할 때 관여함
    • 접근자 프로퍼티로 프로퍼티 값에 접근하는 과정

      1. 프로퍼티 키 유효한지 확인
        • 문자열 || 심벌
      2. 프로토타입 체인에서 프로퍼티 검색
      3. 프로퍼티가 데이터인지 접근자인지 확인
      4. 접근자 프로퍼티의 프로퍼티 어트리뷰트 [[Get]]의 값, getter를 호출해서 그 결과를 반환함.
        • Object.getOwnPropertyDescriptor가 반환하는 프로퍼티 디스크립터 객체의 get 프로퍼티 값과 같음
  • 접근자 프로퍼티와 데이터 프로퍼티 구별

    • getOwnPropertyDescriptor가 반환한 프로퍼티 어트리뷰트 객체를 표현한 프로퍼티 디스크립터 객체가 서로 다름
    • 일반 객체의 proto는 접근자, 함수 객체의 prototype은 데이터

프로퍼티 정의

  • 새로운 프로퍼티 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의 || 기존 프로퍼티의 프로퍼티 어트리뷰트 재정의

  • 객체의 프로퍼티가 어떻게 동작해야 하는지 명확히 정의할 수 있음

  • Object.defineProperty 메서드로 프로퍼티 어트리뷰트 정의

    • 인수로 객체의 참조, 데이터 프로퍼티의 키인 문자열, 프로퍼티 디스크립터 객체 전달

    • 프로퍼티 디스크립터 객체의 프로퍼티 일부 생략 가능

      프로퍼티 디스크립터
      객체의 프로퍼티
      대응하는 프로퍼티
      어트리뷰트
      생략했을 때의
      기본값
      value[[Value]]undefined
      get[[Get]]undefined
      set[[Set]]undefined
      writable[[Writable]]false
      enumerable[[Enumerable]]false
      configurable[[Configurable]]false
    • Object.defineProperty는 한 번에 하나의 프로퍼티, defineProperties는 여러 개의 프로퍼티 한 번에 정의 가능

객체 변경 방지

  • 객체는 재할당 없이 직접 변경 가능
    • 프로퍼티 추가, 삭제, 갱신, 프로퍼티 어트리뷰트 재정의 가능
  • 객체의 변경을 방지하는 다양한 메서드
구분메서드추가삭제값 읽기값 쓰기어트리뷰트
재정의
객체 확장 금지Object.preventExtensionsXOOOO
객체 밀봉Object.sealXXOOX
객체 동결Object.freezeXXOXX

객체 확장 금지

  • 프로퍼티 추가 금지
    • 프로퍼티 동적 추가와 Object.defineProperty 둘 다 금지됨
  • Object.isExtensible로 확장 가능 여부 확인 가능

객체 밀봉

  • 프로퍼티 추가, 삭제, 프로퍼티 어트리뷰트 재정의 금지
    • 읽기, 쓰기만 가능
  • Object.isSealed로 밀봉 여부 확인 가능

객체 동결

  • 읽기만 가능
  • Object.isFrozen으로 동결 여부 확인 가능

불변 객체

  • 위의 변경 방지 메서드들은 얕은 변경방지

    • 직속 프로퍼티만 변경 방지, 중첩 객체까지는 영향 못줌

    • ex. Object.freeze 사용해도 객체 속 객체는 동결할 수 없음

    • const student = {
        name: 'Dory',
        major: {
          college: 'engineering',
          department: 'computer science'
        }
      }
      
      Object.freeze(student);
      
      student.major.department = 'computer engineering'
    • 중첩 객체까지 적용해서 읽기 전용의 불변 객체를 구현하려면 객체를 값으로 갖는 모든 프로퍼티의 재귀적으로 freeze 호출해야됨,,

좋은 웹페이지 즐겨찾기