[240317] 16장 프로퍼티 어트리뷰트 (2)

### 접근자 프로퍼티

  • 자체적으로는 값을 갖고 있지 않음
  • 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티
  • 접근자 프로퍼티는 다음과 같은 프로퍼티 어트리뷰트를 갖는다.
    • [[Get]]
      • 프로퍼티 디스크립터 객체의 프로퍼티 : get
      • 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 읽을 때 호출되는 접근자 함수
      • 접근자 프로퍼티 키로 프로퍼티 값에 접근하면 프로퍼티 어트리뷰트 [[Get]] 의 값, 즉 getter 함수가 호출되고 그 결과 프로퍼티 값으로 반환된다.
    • [[Set]]
      • set
      • 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 지정할 때 호출되는 접근자 함수다.
      • 프로퍼티 키로 프로퍼티 값을 저장하면 setter 함수가 호출되고 프로퍼티 값으로 지정된다.
    • [[Enumerable]]
      • enumerable
      • 데이터 프로퍼티의 [[Enumerable]] 과 동일
      • 데이터 프로퍼티의 [[Configurable]] 과 동일
const person = {
	// 데이터 프로퍼티
	firstName: 'Ungmo',
	lastName: 'Lee',
	
	// fullName 은 접근자 함수로 구정된 접근자 프로퍼티
	// getter 함수
	get fullName() {
		return `${this.firstName} ${this.lastName}`;
	}, 
	
	// setter 함수
	set fullName(name) {
	// 배열 디스크럭처링 할당
	[this.firstName, this.lastName] = name.split(' ');
	}
}; 

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

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

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

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

// fullName은 접근자 프로퍼티다.
// 접근자 프로퍼티는 get, set, enumberable, configurable 
// 프로퍼티 어트리뷰트를 갖는다.
let descriptor = Object.getOwnPropertyDescriptor(person, 'fristName');
console.log(descriptor);
// {get: f, set: f , enumerable: true, configurable: true}

  • firstName , lastName 은 일반적인 데이터 프로퍼티
  • get set 이 붙은 메서드는 getter , setter 함수
  • getter/setter 함수의 이름 fullName 이 접근자 프로퍼티임
    • value 어트리뷰트를 가지지 않는다.
    • 데이터 프로퍼티의 값을 읽거나 저장할 때 관여
    • ex) 슬롯/메서드 관점으로 동작 방식
      1. 프로퍼티 키가 유효한지 확인, 프로퍼티 키는 문자열 또는 심벌이어야한다. “fullName” 은 문자열이므로 유효한 프로퍼티 키
      2. 프로토타입 체인에서 프로퍼티를 검색, person 객체에 fullName 프로퍼티가 존재
      3. 검색된 fullName 프로퍼티가 데이터 프로퍼티인지 접근자 프로퍼티인지 확인
      4. 접근자 프로퍼티 fullName의 프로퍼티 어트리뷰트 [[Get]] 의 값, 즉 getter 함수를 호출하여 그 결과 값 반환. 프로퍼티 fullName의 프로퍼티 어트리뷰트 Get 의 값은 Object.getOwnPropertyDescriptor 메서드가 반환하는 프로퍼티 디스크립트 객체의 ㅎet 프로퍼티 값과 같다.
  • 접근자 프로퍼티와 데이터 프로퍼티를 구별하는 방법
    • 디스크립터 객체의 프로퍼티가 다름
    • TODO: 예제 16-07 이해 안감, 프로토타입장 공부할때 다시 보기

프로퍼티 정의

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

// 인수 : 객체의 참조, 데이터 프로퍼티의 키인 문자열, 프로퍼티 디스크립터 객체 
// 디스크립터 객체를 전달하지 않으면 false 로 들어감
Object.defineProperty(person, 'firstName', {
value: 'Ungmo',
writable: true,
enumerable: true,
configurable: true,
});

// 접근자 프로퍼티 정의
Object.defineProperty(person, 'firstName', {
	get() {
		return `${this.firstName} ${this.lastName}`;
	},
	set(name) {
		[this.firstName, this.lastName] = name.split(' ');
	},
	enumerable: true,
	configurable: true
});
  • Object.defineProperties 메서드를 사용하면 여러 개의 프로퍼티 한 번에 정의 가능

객체 변경 방지

  1. 객체 확장 금지
    • Object.preventExtensions
    • 프로퍼티 추가를 금지한다.
    • Object.defineProperty , 프로퍼티 동적 추가 금지
    • Object.isExtensible 메서드로 확인 가능
const person = { name: 'Lee' };

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

// person 객체의 확장을 금지
Object.preventExtensions(person);

console.log(Object.isExtensible(person)); // false
  1. 객체 밀봉
    • Object.seal
    • 밀봉된 객체는 읽기와 쓰기만 가능하다.
    • Object.isSealed 메서드로 확인 가능
  2. 객체 동결
    • Object.freeze
    • 동결된 객체는 읽기만 가능하다.
    • Object.isFrozen 메서드로 확인가능

불변 객체

  • 지금까지 살펴본 변경 방지 메서드들은 얕은 변경 방지로 직속 프로퍼티만 변경이 방지되고 중첩 객체까지는 영향을 주지 못한다.
// ex) Object.freeze 로 동결하여도 중첩 객체까지 동결할 수 없다.
const person = {
	name: 'Lee',
	address: { city: 'Seoul' }
};

Object.freeze(person);

console.log(Object.isFrozen(person));  // true
console.log(Object.isFrozen(person.address));  // false
  • 중첩 객체까지 동결하고 싶으면 재귀적으로 모든 프로퍼티에 대해 Object.freeze 메서드를 호출해야함
function deepFreeze(target) {
if(target && typeof target === 'object' && !Object.isFrozen(target)) {
	Object.freeze(target);
	Object.keys(target).forEach(key=> deepFreezze(target[key]));
}
return target;
}

const person = {
name: 'Lee',
address: {city: 'Seoul'}
};

deepFreeze(person);

Categories:

Updated:

Leave a comment