강의 노트/JS

[JS deep dive] 16 프로퍼티 어트리뷰트

매망쩔 2023. 3. 30. 03:20

내부 슬롯과 내부 메서드

자바스크립트 엔진의 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사용하는 의사 프로퍼티와 의사 메서드

이중 대괄호([[...]])로 감싼 이름들이 내부 슬롯과 내부 메서드이다.

내부 슬롯과 내부 메서드는 ECMAScript사양에  정의된 대로 구현되어 자바스크립트 엔진에서  실제로 동작하지만, 개발자가 직접 접근할 수 있도록 외부로 공개된 객체의 프로퍼티는 아니다.

 

예외적으로 일부는 접근 가능함 (ex [[Prototype]] 내부 슬롯의 경우 __proto__를 통해 간접 접근 가능)

 

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

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

프로퍼티 상태 : value (값) , writable(값의 갱신 가능성) , enumerable(열거 가능 여부), configurable(재정의 가능 여부)

Object.getOwnPropertyDescriptor메서드를 이용해 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터 객체를 통해서 간접 확인 가능 (ES8부터는 Object.getOwnPropertyDescriptors메스드를 통해, 모든 프로퍼티의 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터 객체를 반환 가능)

 

 

 

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

1 .데이터 프로퍼티 

키와 값으로 구성된 일반적인 프로퍼티

아래와 같은 프로퍼티 어트리뷰트를 가지며, 자바스크립트 엔진이 프로퍼티를 생성할 때 기본값으로 자동 정의됨

 

 프로퍼티
어트리뷰트
프로퍼티 디스크립터
객체의 프로퍼티
설명
[[Value]] Value
프로퍼티 키를 통해 프로퍼티 값에 접근하면 반환되는 값
프로퍼티 키를 통해 프로퍼티 값을 변경하면 [[Value]]에 값을 재할당함. 이때 프로퍼티가 없으면 프로퍼티를 동적 생성하고 생성된 프로퍼티의 [[Value]]에 값을 저장함

[[Writable]] writable
프로퍼티 값의 변경 가능 여부를 나타내며 불리언 값을 갖음
[[Writable]]의 값이 false인 경우 해당 프로퍼티의 [[Value]]의 값을 변경할 수 없는 읽기 전용 프로퍼티가 됨
[[Enumerable]] enumerable
프로퍼티의 열거 가능 여부를 나타내며 불리언 값을 갖음
[[Enumerable]]의 값이 false인 경우 해당 프로퍼티는 for…in 문이나 Object.keys 매서드 등으로 열거 불가
[[Configurable]] Configurable
프로퍼티의 재정의 가능 여부를 나타내며 불리언 값을 갖음
[[Configurable]]의 값이 false인 경우 프로퍼티의 삭제 프로퍼티의 어트리뷰트 값의 변경이 금지됨. [[Writable]]의 값이 true인 경우 [[Value]]의 변경과 [[Writable]]false로 변경은 가능함

 

접근자 프로퍼티 

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

 

 프로퍼티
어트리뷰트
프로퍼티 디스크립터
객체의 프로퍼티
설명
[[Get]] get 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 읽을 때 호출되는 접근자 함수. , 접근자 프로퍼티 키로 프로퍼티 값에 접근하면 프로퍼티 어트리뷰트 [[Get]]의 값, getter 함수가 호출되고 그 결과가 프로퍼티 값으로 반환됨.
[[Set]] set 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 함수. , 접근자 프로퍼티 키로 프로퍼티 값을 저장하면 프로퍼티 어트리뷰트 [[Set]]의 값, setter함수가 호출되고 그 결과가 프로퍼티 값으로 저장됨
[[Enumerable]] enumerable
프로퍼티의 열거 가능 여부를 나타내며 불리언 값을 갖음
[[Enumerable]]의 값이 false인 경우 해당 프로퍼티는 for…in 문이나 Object.keys 매서드 등으로 열거 불가
[[Configurable]] Configurable
프로퍼티의 재정의 가능 여부를 나타내며 불리언 값을 갖음
[[Configurable]]의 값이 false인 경우 프로퍼티의 삭제 프로퍼티의 어트리뷰트 값의 변경이 금지됨.

 

접근자 함수는 getter/setter함수라고도 부름.

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

  //fullName은 접근자 함수로 구성된 프로퍼티임
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  },

  set fullName(name) {
    [this.firstName, this.lastName] = name.split(" ");
  },
};

console.log(Object.getOwnPropertyDescriptor(person, "firstName"));
console.log(Object.getOwnPropertyDescriptor(person, "fullName"));

 

person객체의 firstName과 lastName은 일반적인 데이터 프로퍼티이다.

메서드 앞에 get, set이 붙은 메서드가 getter와 setter함수이고, getter/setter 함수의 이름 fullName이 접근자 프로퍼티이다.

 

내부슬롯/메서드 관점으로 설명시, 접근자 프로퍼티로 프로퍼티 값에 접근하면 내부 메서드가 호출되어 아래와 같이 동작함

1. 프로퍼티 키가 유효한지 확인. (키는 문자열  또는심벌이어야 함)

2. 프로토타입 체인에서 프로퍼티 검색

3. 검색된 fullName프로퍼티가 데이터 프로퍼티인지 접근자 프로퍼티인지 확인

4. 접근자 프로퍼티의 프로퍼티 어트리뷰트의 값을 호출하여 그 결과를 반환

 

일반 객체의 __proto__ 는 접근자 프로퍼티

함수 객체의 prototype은 데이터 프로퍼티임

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

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

Object.defineProperty 메서드를 사용하여 정의할 수 있음.(인수 : 객체의 참조, 데이터 프로퍼티의 키, 프로퍼티 디스크립터 객체)

const person = {};

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

Object.defineProperty(person, "lastName", {
  value: "Lee",
  //디스크립터 객체 프로퍼티를 누락시키면 undefined false가 기본값
});

프로퍼티 디스크립터 객체의 프로퍼티중 value get set은 생략시 undefined 
writable enumerable configurable 은 생략시 false가 기본값이다.

 

Object.defineProperties를 통해 한번에 여러개의 프로퍼티를 정의할 수 있음

 

객체 변경 방지

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

자바스크립트는 객체의 변경을 방지하는 메서드를 제공하고, 각각의 메서드 별로 변경을 금지하는 강도가 다르다.

 

구분 메서드 프로퍼티
추가
프로퍼티
삭제
프로퍼티
값 읽기
프로퍼티
값 쓰기
프로퍼티 어트리뷰트 재정의
객체 확장 금지 Object.preventExtensions X O O O O
객체 밀봉 Object.seal X X O O X
객체 동결 Object.freeze X X O X X

 

프로퍼티 추가는 동적 추가와 Object.defineProperty로 가능한데, 두 방법 모두 불가능해짐

객체 확장 금지는 Object.isExtensible 메서드로 확인가능(true면 확장가능)

객체 밀봉 여부는 Object.isSealed 메서드로 확인 가능

객체 동결 여부는 Obejct.isFrozen으로 확인 가능

 

불변객체

위의 메서드들은 얕은 변경 방지로 직속 프로퍼티 변경이 방지되고 중첩 객체까지는 영향을 주지 못함.

중첩 객체까지 동결하여 변경이불가능한 읽기 전용의불변 객체를 구현하려면, 객체를 값으로 갖는 모든 프로퍼티에 대해 재귀적으로 Object.freeze 메서드를 호출해야 함

function deepFreeze(target) {
  //객체가 아니거나 동결된 객체는 무시하고 객체이고 동결되지 않은 객체만 동결
  if (target && typeof target === "object" && !Object.isFrozen(target)) {
    Object.freeze(target);
    Object.keys(target).forEach((key) => deepFreeze(target[key]));
  }
  return target;
}