티스토리 뷰
16장. 프로퍼티 어트리뷰트
16-1. 내부 슬롯과 내부 메서드
내부 슬롯과 내부 메서드는 JS 엔진의 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사양하는 의사 프로퍼티(pseudo property) 와 의사 메서드(pseudo method)이다.
내부 슬롯과 내부 메서드는 JS 엔진의 내부 로직이므로 원칙적으로 JS는 직접적으로 접근하거나 호출할 수 있는 방법을 제공하지 않는다.
모든 객체는 [[Prototype]]이라는 내부 슬롯을 갖는다. 원칙적으로 직접 접근 할 수 없지만 __proto__
를 통해 간접적으로 접근할 수 있다.
const o = {};
o.__proto__ // Object.prototype
16-2. 프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체
JS 엔진은 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰트를 기본값으로 자동 정의한다. 프로퍼티 상태란 value, writable, enumerable, configurable 을 말한다.
프로퍼티 어트리뷰트는 JS 엔진이 관리하는 내부 상태 값인 내부 슬롯 [[Value]]
, [[Writable]]
, [[Enumerable]]
, [[Configurable]]
이다.
프로퍼티 어트리뷰트에 직접 접근할 수는 없지만 Object.getOwnPropertyDescriptor()
를 사용해 간접적으로 확인 할 수 있다.
// 1.
const person = {
name: 'Lee'
};
console.log(Object.getOwnPropertyDescriptor(person, 'name')) ;
// {value: 'Lee', writable: true, enumerable: true, configurable: true}
// 2. ES8
const person = {
name: 'Lee'
};
person.age = 20;
console.log(Object.getOwnPropertyDescriptor(person) ;
// name: {value: 'Lee', writable: true, enumerable: true, configurable: true}
// age: {value: 20, writable: true, enumerable: true, configurable: true}
16-3. 데이터 프로퍼티와 접근자 프로퍼티
데이터 프로퍼티
JS 엔진이 프로퍼티를 생성할 때 기본값으로 자동 정의된다.
default 값은 프로퍼티를 동적 추가해도 마찬가지이다.
접근자 프로퍼티
접근자 프로퍼티 (accessor property)는 자체적으로 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수 (access function)로 구성된 프로퍼티다.
접근자 함수는 getter/setter 함수라고도 부른다. getter, setter 모두를 정의할 수 도 있고 하나만 정의할 수도 있다.
const person = {
firstName: 'Subin',
lastName: 'Lee',
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
set fullName() {
[this.firstName, this.lastName] = name.split(' ');
}
}
// setter 호출됨.
person.fullName = 'Subin Lee';
// getter 호출됨.
console.log(person.fullName);
Object.get
접근자 프로퍼티로 프로퍼티 값에 접근하는 과정
- 내부적으로 [[Get]] 내부 메서드가 호출되어 다음과 같이 동작한다.
- 프로퍼티 키가 유효한지 확인. 문자열 또는 심볼이어야함. “fullName”은 문자열이므로 유효한 프로퍼티 키이다.
- 프로토타입 체인에서 프로퍼티를 검색한다. person 객체에 fullName 프로퍼티가 존재한다.
- fullName 프로퍼티가 데이터 프로퍼티인지 접근자 프로퍼티인지 확인한다. fullName 프로퍼티는 접근자 프로퍼티이다.
- 접근자 프로퍼티 fullName의 프로퍼티 어트리뷰트 값, 즉 getter 함수를 호출하여 그 결과를 반환한다. fullName의 프로퍼티 어트리뷰트 [[Get]] 의 값은
Object.getOwnPropertyDescriptor()
가 반환하는 프로퍼티 디스크립터 객체의 get 프로퍼티 값과 같다.
접근자 프로퍼티와 데이터 프로퍼티 구별하는 방법
메서드가 리턴한 프로퍼티 디스크립터 객체를 보고 알 수 있다.
// 일반 객체의 __proto__ 는 접근자 프로퍼티이다.
Object.getOwnPropertyDescriptor(Object.prototype, '__proto__');
// {get: f, set: f, emi,erab;e: false, configuralbe: true}
// 함수 객체의 prototype은 데이터 프로퍼티이다.
Object.getOwnPropertyDescriptor(function() {}, 'prototype');
// {value: {...}, writable: true, enumerable: false, configurable: false}
16-4. 프로퍼티 정의
const person = {};
// doc
defineProperty<T>(o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType<any>): T;
// 1. 데이터 프로퍼티 정의
Object.defineProperty(person, 'firstName', {
value: 'first',
writable: true,
enumerable: true,
configurable: true
});
// 디스크립터 객체의 프로퍼티를 누락시키면 undefined, false가 기본값
Object.defineProperty(person, 'lastName', {
value: 'last',
});
// [[Enumerable]] 이 false 이면
// 해당 프로퍼티는 for...in, Object.keys 등으로 열거할 수 없다.
// [[Writable]] 이 false 이면 [[Value]]의 값을 변경할 수 없다.
// [[Configurable]] 이 false 이면 해당 프로퍼티 재정의, 삭제 할 수 없다.
// 2. 접근자 프로퍼티 정의
Object.defineProperty(person, 'fullName', {
get() {
return `${this.firstName} ${this.lastName}`;
},
set(name) {
[this.firstName, this.lastName] = name.split(' ');
},
enumerable: true,
configurable: true
});
- 프로퍼티 디스크립터 객체에서 생략된 어트리뷰트의 기본값
// 여러 개의 프로퍼티를 한 번에 정의: Object.defineProperties()
const person = {};
Object.defineProperties(person, {
firstName: {
value: 'first',
writable: true,
enumerable: true,
configurable: true
},
lastName: {
value: 'last',
writable: true,
enumerable: true,
configurable: true
},
fullName: {
get() {
return `${this.firstName} ${this.lastName}`;
},
set(name) {
[this.firstName, this.lastName] = name.split(' ');
},
enumerable: true,
configurable: true
},
});
16-5. 객체 변경 방지
JS는 객체 변경을 방지하는 다양한 메서드를 제공한다.
// Object.preventExtensions
const person = { name: 'Lee' };
Object.isExtensible(person); // true
Object.preventExtensions(person);
Object.isExtensible(person); // false
person.age = 20; // 무시
delete person.name; // 가능
Object.defineProperty(person, 'age', { value: 20 }); // error
// Object.seal
const person = { name: 'Lee' };
Object.isSealed(person); // false
Object.seal(person);
Object.isSealed(person); // true
// 밀봉되면 configurable 이 false 이다.
person.age = 20; // 추가 금지
delete person.name; // 삭제 금지
person.name = 'Kim'; // 갱신 가능
// 어트리뷰트 재정의 금지
Object.defineProperty(person, 'name', { configurable: true });
// Object.freeze
const person = { name: 'Lee' };
Object.isFrozen(person); // false
Object.freeze(person);
Object.isFrozen(person); // true
// 동결되면 writable, configurable 이 false 이다.
person.age = 20; // 추가 금지
delete person.name; // 삭제 금지
person.name = 'Kim'; // 갱신 금지
// 어트리뷰트 재정의 금지
Object.defineProperty(person, 'name', { configurable: true });
불변 객체
위 메서드들은 얕은 변경 방지 (shallow only)로 직속 프로퍼티만 변경이 방지되고 중첩 객체까지는 영향을 주지 못한다.
// Object.freeze로 객체를 동결하여도 중첩 객체까지는 동결할 수 없다.
const person = {
name: 'Lee',
address: { city: 'Seoul' }
}
Object.freeze(person);
Object.isFrozen(person); // true
Object.isFrozen(person.address); // false
person.address.city = 'Busan';
// 중첩 객체까지 동결하여 변경이 불가능한 읽기 전용의 불변 객체를 구현하려면
// 객체를 값으로 갖는 모든 프로퍼티에 대해 재귀적으로 Object.freeze 를 호출해야 한다.
function deepfreeze(target) {
if (target && typeof target === 'object' && !Object.isFrozen(target)) {
Object.freeze(target);
Object.ketys(target).forEach(key => deepFreeze(target[key]));
}
return target;
}
나온 메서드 전부 쓸 일이 있을지는 잘 모르겠는데 그래도 나중에 나왔을때 뭔지는 알아야 하니 한 번씩 읽고 써보고 넘어감.
'책 > 모던 자바스크립트 딥다이브' 카테고리의 다른 글
18장. 함수와 일급 객체 (2) | 2022.04.24 |
---|---|
17장. 생성자 함수에 의한 객체 생성 (0) | 2022.04.24 |
15장. let, const 키워드와 블록 레벨 스코프 (0) | 2022.04.24 |
14장. 전역 변수의 문제점 (0) | 2022.04.24 |
4장. 변수 (0) | 2022.04.24 |
- Total
- Today
- Yesterday
- js promise
- java
- 모던자바스크립트
- REST API
- JS 딥다이브
- HTTP 완벽가이드
- 이펙티브자바
- GCP
- js api
- 집 구하기
- Spring Security
- BOJ
- 패스트캠퍼스 컴퓨터공학 완주반
- 이펙티브자바 아이템60
- 백기선 스터디
- 김영한 JPA
- 드림코딩
- HTTP 완벽 가이드
- JPA 연관관계 매핑
- 이펙티브자바 아이템59
- 프로그래머스
- 프로그래머스 SQL
- 킹수빈닷컴
- http
- js array
- 김영한 http
- 가상 면접 사례로 배우는 대규모 시스템 설계 기초
- 이펙티브자바 스터디
- dreamcoding
- 백준
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |