fffo
프로퍼티 어트리뷰트 본문
프로퍼티 어트리뷰트
내부 슬롯과 내부 메서드
- js 엔진의 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사용하는 의사(pseudo) 프로퍼티와 의사 메서드임
- 이중 대괄호[[...]]로 감싸서 표현
- js엔진에서 실제로 동작하지만 개발자가 직접 접근할 수는 없음 → 간접적 접근이 가능한 일부 내부 슬롯과 내부 메서드 존재 ([[Prototype]]을 proto를 통해 접근 가능)
프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체
- js 엔진은 프로퍼티를 생성할 때 프로퍼티 상태를 나타내는 프로퍼티 어트리뷰트를 기본값으로 자동 정의함
- 프로퍼티 어트리뷰트는 js 엔진이 관리하는 내부 상태 값(meta-property)인 내부 슬롯들로 이루어짐
- 프로퍼티 어트리뷰트에 직접 접근할 수 없지만 Object.getOwnPropertyDescriptor 메서드를 이용해 간접적으로 확인 가능
Object.getOwnPropertyDescriptor() 메서드
- 첫 번째 매개변수에 객체의 참조 전달, 두 번째 매개변수에 프로퍼티 키를 문자열로 전달
- 프로퍼티 디스크립터 객체를 반환
- ES8에서 도입된 Object.getOwnPropertyDescriptors 메서드(메서드 이름에 s가 추가됨)는 모든 프로퍼티의 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터 객체를 반환함
const person = {
name: 'dlwlrma'
};
person.age = 29;
console.log(Object.getOwnPropertyDescriptor(person, "age"));
// { value: 29, writable: true, enumerable: true, configurable: true }
console.log(Object.getOwnPropertyDescriptors(person));
// {
// name: { value: 'dlwlrma', writable: true, enumerable: true, configurable: true },
// age: { value: 29, writable: true, enumerable: true, configurable: true }
// }
데이터 프로퍼티와 접근자 프로퍼티
- 프로퍼티는 데이터 프로퍼티와 접근자 프로퍼티로 구분할 수 있음
- 데이터 프로퍼티 : 키와 값으로 구성된 프로퍼티
- 접근자 프로퍼티 : 자체적으로 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 호출되는 접근자 함수로 구성된 프로퍼티
데이터 프로퍼티
- 데이터 프로퍼티는 다음의 프로퍼티 어트리뷰트를 가짐
- [[Value]] : 프로퍼티 키를 통해 프로퍼티 값에 접근하면 반환되는 값
- [[Writable]] : 값의 갱신 가능 여부를 나타내는 불리언 값
- [[Enumerable]] : 열거 가능 여부를 나타내는 불리언 값
- false일 경우 해당 프로퍼티는 for ... in 문이나 Object.keys 메서드 등으로 열거할 수 없음
- [[Configurable]] : 재정의 가능 여부를 나타내는 불리언 값
- false일 경우 해당 프로퍼티의 삭제, 프로퍼티 어트리뷰트 값의 변경 금지
- 단, [[Writable]]이 true일 경우 [[Value]]의 변경과 [[Writable]]을 false로 변경하는 것은 허용됨
- 프로퍼티가 생성될 때 [[Value]]의 값은 프로퍼티 값으로, 나머지는 모두 true로 초기화됨
접근자 프로퍼티
- 접근자 프로퍼티는 다음의 프로퍼티 어트리뷰트를 가짐
- [[Get]] : 접근자 프로퍼티 키로 프로퍼티 값에 접근 시 프로퍼티 어트리뷰트 [[Get]]의 값인 getter함수가 호출됨
- [[Set]] : 접근자 프로퍼티 키로 프로퍼티 값을 저장 시 프로퍼티 어트리뷰트 [[Set]]의 값인 setter함수가 호출됨
- [[Enumerable]] : 데이터 프로퍼티와 같음
- [[Configurable]] : 데이터 프로퍼티와 같음
const person = {
firstName: 'wlrma',
lastName: 'dl',
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
set fullName(name) {
[this.firstName, this.lastName] = name.split(' ');
},
};
// console.log(person.fullName()); 괄호를 붙이면 안됨
console.log(person.fullName); // wlrma dl
person.fullName = "sanghun moon";
console.log(person.firstName); // sanhun
console.log(person.fullName); // sanhun moon
console.log(Object.getOwnPropertyDescriptor(person, "fullName"));
// { get: [Function: get fullName],
// set: [Function: set fullName],
// enumerable: true,
// configurable: true
// }
- 위의 코드에서
- firstName, lastName은 데이터 프로퍼티
- fullName은 접근자 프로퍼티
프로퍼티 정의
- 새로운 프로퍼티를 추가할 때 프로퍼티 어트리뷰트를 명시정으로 정의하거나 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의 → 객체의 프로퍼티가 어떻게 동작하는지 명확히 정의 가능
Object.defineProperty
- 프로퍼티를 정의하는 메서드
- 객체 참조, 프로퍼티 키 문자열, 프로퍼티 디스크립터 객체 순으로 인자 전달
const { first, set } = require("lodash");
const person = {};
Object.defineProperty(person,'firstName', {
value: "wlrma",
writable: true,
enumerable: true,
configurable: true,
});
Object.defineProperty(person,"lastName", {
value: "dl",
})
let descriptor = Object.getOwnPropertyDescriptor(person,"lastName");
console.log(descriptor);
// {
// value: 'dl',
// writable: false,
// enumerable: false,
// configurable: false
// }
person.lastName = "wj"; //무시됨
console.log(person.lastName); // dl
console.log(Object.keys(person)); // [ 'firstName' ]
Object.defineProperty(person, 'fullName', {
get() {
return `${firstName} ${lastName}`;
},
set(name) {
[this.firstName, this.lastName] = name.split(' ');
},
enumerable: true,
configurable: true,
});
- writable 이 false값일 때 value를 재할당 시 error가 아닌 무시됨을 주의
- Object.defineProperty 메서드에서 각 프로퍼티 어트리뷰트의 기본 값
- value : undefined
- get : undefined
- set : undefined
- writable : false
- enumerable : false
- configurable : false
- Object.defineProperties 메서드로 2개 이상의 프로퍼티를 한 번에 정의할 수 있음
객체 변경 방지
- 객체는 변경 가능한 값임. 이에 따라 변경을 방지하는 방법도 존재
객체 확장 금지
- Object.preventExtensions( {객체} ) : 프로퍼티 추가가 금지됨
- 프로퍼티 동적 추가 금지, Object.defineProperty 메서드 금지
- Object.isExtensible 메서드 : 객체 확장 가능 여부 불리언 값으로 반환
- 프로퍼티 동적 추가 시 에러가 아닌 무시(strict mode에서는 에러)
- Object.defineProperty 메서드로 추가시 타입 에러
객체 밀봉
- Object.seal( {객체} ) : 객체 확장 금지 + 프로퍼티 삭제, 프로퍼티 어트리뷰트 재정의 금지
- 밀봉된 객체는 읽기와 쓰기만 가능
- Object.isSealed 메서드 : 객체 밀봉 여부 불리언 값으로 반환
- 프로퍼티 추가와 삭제는 무시되지만(strict mode에선 에러) 프로퍼티 어트리뷰트 재정의 시 type error
객체 동결
- Object.freeze( {객체} ) : 객체 밀봉 + 프로퍼티 값 갱신 금지
- 동결된 객체는 읽기만 가능
- Object.isFrozen 메서드 : 객체 동결 여부 불리언 값으로 반환
const person = {};
Object.defineProperty(person,'firstName', {
value: "wlrma",
writable: true,
enumerable: true,
configurable: true,
});
Object.preventExtensions(person);
person.hobby = "singing"; // 무시
Object.defineProperty(person, 'hobby', {
value: "singing"
})
// TypeError: Cannot define property hobby, object is not extensible
Object.seal(person);
delete person.firstName; // 무시
Object.defineProperty(person, 'firstName', {
configurable: true,
});
// TypeError: Cannot redefine property: firstName
Object.freeze(person);
console.log(Object.isExtensible(person)); // false
console.log(Object.isSealed(person)); // true
console.log(Object.isFrozen(person)); // true
console.log(Object.getOwnPropertyDescriptor(person, 'firstName'));
// { value: 'wlrma', writable: false, enumerable: true, configurable: false }
// writable과 configurable이 false가 됨
불변 객체
- 위의 메서드들은 얕은 변경 방지(shallow only)로 직속 프로퍼티만 변경 방지되고 중첩 객체는 방지 안됨.
- 객체의 중첩 객체까지 변경 불가능한 읽기 전용의 불변 객체를 구현하려면 객체를 값으로 갖는 모든 프로퍼티에 대해 재귀적으로 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;
}
const person = {
name: 'BDNS',
address: { city: 'Huam' },
};
deepFreeze(person);
console.log(isFrozen(person.address)); // true
'Programming > Javascript' 카테고리의 다른 글
함수의 일급 객체 (0) | 2021.10.02 |
---|---|
생성자 함수에 의한 객체 생성 (0) | 2021.10.01 |
전역변수의 문제점 / let, const 키워드와 블록 스코프 (0) | 2021.09.29 |
스코프 (0) | 2021.09.28 |
js 복습 - 깊은복사, 생성자, Date (0) | 2021.09.27 |
Comments