우리가 알고 있듯이 객체는 속성을 저장할 수 있습니다.
지금까지 속성은 단순한 "키-값" 쌍이었습니다. 그러나 객체 속성은 실제로 더 유연하고 강력한 것입니다.
이번 장에서는 추가적인 구성 옵션을 연구하고, 다음 장에서는 눈에 띄지 않게 이 옵션을 getter/setter 함수로 변환하는 방법을 살펴보겠습니다.
value
외에 객체 속성에는 세 가지 특수 속성(소위 "플래그")이 있습니다.
writable
- true
이면 값을 변경할 수 있고, 그렇지 않으면 읽기 전용입니다.
enumerable
- true
이면 루프에 나열되고, 그렇지 않으면 나열되지 않습니다.
configurable
– true
인 경우 속성을 삭제하고 이러한 속성을 수정할 수 있으며, 그렇지 않은 경우에는 수정할 수 없습니다.
일반적으로 그들은 나타나지 않기 때문에 우리는 아직 그것을 보지 못했습니다. "일반적인 방법"으로 속성을 생성하면 모든 속성이 true
입니다. 하지만 언제든지 변경할 수도 있습니다.
먼저 해당 플래그를 얻는 방법을 살펴보겠습니다.
Object.getOwnPropertyDescriptor 메소드를 사용하면 속성에 대한 전체 정보를 쿼리할 수 있습니다.
구문은 다음과 같습니다.
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
obj
정보를 가져올 개체입니다.
propertyName
속성의 이름입니다.
반환된 값은 소위 "속성 설명자" 개체입니다. 여기에는 값과 모든 플래그가 포함됩니다.
예를 들어:
사용자 = { 이름: "존" }; let descriptor = Object.getOwnPropertyDescriptor(사용자, '이름'); 경고( JSON.stringify(descriptor, null, 2 ) ); /* 속성 설명자: { "값": "존", "쓰기 가능": 사실, "열거 가능": 사실, "구성 가능": 사실 } */
플래그를 변경하려면 Object.defineProperty를 사용할 수 있습니다.
구문은 다음과 같습니다.
Object.defineProperty(obj, propertyName, 설명자)
obj
, propertyName
설명자를 적용할 개체 및 해당 속성입니다.
descriptor
적용할 속성 설명자 개체입니다.
속성이 존재하는 경우 defineProperty
해당 플래그를 업데이트합니다. 그렇지 않으면 주어진 값과 플래그를 사용하여 속성을 생성합니다. 이 경우 플래그가 제공되지 않으면 false
로 간주됩니다.
예를 들어, 여기에서는 모든 거짓 플래그를 사용하여 속성 name
생성됩니다.
사용자 = {}로 두십시오; Object.defineProperty(사용자, "이름", { 값: "존" }); let descriptor = Object.getOwnPropertyDescriptor(사용자, '이름'); 경고( JSON.stringify(descriptor, null, 2 ) ); /* { "값": "존", "쓰기 가능": 거짓, "열거 가능": 거짓, "구성 가능": 거짓 } */
위의 "정상적으로 생성된" user.name
과 비교해 보세요. 이제 모든 플래그가 거짓입니다. 이것이 우리가 원하는 것이 아니라면 descriptor
에서 true
로 설정하는 것이 좋습니다.
이제 예제를 통해 플래그의 효과를 살펴보겠습니다.
writable
플래그를 변경하여 user.name
쓰기 불가능(재할당할 수 없음)으로 만들어 보겠습니다.
사용자 = { 이름: "존" }; Object.defineProperty(사용자, "이름", { 쓰기 가능: 거짓 }); user.name = "피트"; // 오류: 'name' 속성을 읽기 전용으로 할당할 수 없습니다.
이제 자신의 defineProperty
적용하여 우리 이름을 재정의하지 않는 한 누구도 사용자 이름을 변경할 수 없습니다.
오류는 엄격 모드에서만 나타납니다.
Non-Strict 모드에서는 쓰기 불가능한 속성 등에 쓸 때 오류가 발생하지 않습니다. 하지만 작전은 여전히 성공하지 못합니다. 플래그를 위반하는 작업은 엄격하지 않은 경우 자동으로 무시됩니다.
다음은 동일한 예이지만 속성은 처음부터 생성됩니다.
사용자 = { }; Object.defineProperty(사용자, "이름", { 값: "John", // 새로운 속성의 경우 무엇이 사실인지 명시적으로 나열해야 합니다. 열거 가능: 사실, 구성 가능: 사실 }); 경고(사용자.이름); // 존 user.name = "피트"; // 오류
이제 user
에 사용자 정의 toString
추가해 보겠습니다.
일반적으로 객체에 내장된 toString
은 열거 불가능하며 for..in
에 표시되지 않습니다. 그러나 우리 자신의 toString
추가하면 기본적으로 다음과 같이 for..in
에 표시됩니다.
사용자 = { 이름: "존", toString() { this.name을 반환합니다. } }; // 기본적으로 두 속성이 모두 나열됩니다. for(사용자에게 키 입력) Alert(key); // 이름, toString
마음에 들지 않으면 enumerable:false
설정할 수 있습니다. 그러면 내장 루프와 마찬가지로 for..in
루프에 표시되지 않습니다.
사용자 = { 이름: "존", toString() { this.name을 반환합니다. } }; Object.defineProperty(사용자, "toString", { 열거 가능: 거짓 }); // 이제 toString이 사라집니다. for(사용자에게 키 입력) Alert(key); // 이름
열거할 수 없는 속성도 Object.keys
에서 제외됩니다.
경고(Object.keys(사용자)); // 이름
구성할 수 없는 플래그( configurable:false
)는 때때로 내장 객체 및 속성에 대해 사전 설정되어 있습니다.
구성할 수 없는 속성은 삭제할 수 없으며 해당 속성을 수정할 수도 없습니다.
예를 들어 Math.PI
는 쓰기, 열거 및 구성이 불가능합니다.
let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI'); 경고( JSON.stringify(descriptor, null, 2 ) ); /* { "값": 3.141592653589793, "쓰기 가능": 거짓, "열거 가능": 거짓, "구성 가능": 거짓 } */
따라서 프로그래머는 Math.PI
의 값을 변경하거나 덮어쓸 수 없습니다.
수학.PI = 3; // 쓰기 가능하므로 오류: false // 삭제 Math.PI도 작동하지 않습니다.
또한 Math.PI
다시 writable
가능하도록 변경할 수 없습니다.
// 구성 가능으로 인해 오류 발생: false Object.defineProperty(Math, "PI", { 쓰기 가능: true });
Math.PI
로 할 수 있는 일은 전혀 없습니다.
속성을 구성 불가능하게 만드는 것은 일방통행입니다. defineProperty
사용하여 이를 다시 변경할 수 없습니다.
참고: configurable: false
속성 플래그 변경 및 삭제를 방지하는 동시에 해당 값을 변경할 수 있도록 허용합니다.
여기서 user.name
은 구성할 수 없지만 (쓰기 가능하므로) 변경할 수 있습니다.
사용자 = { 이름: "존" }; Object.defineProperty(사용자, "이름", { 구성 가능: 거짓 }); user.name = "피트"; // 잘 작동합니다 사용자 이름 삭제; // 오류
그리고 여기서는 내장 Math.PI
와 마찬가지로 user.name
"영원히 봉인된" 상수로 만듭니다.
사용자 = { 이름: "존" }; Object.defineProperty(사용자, "이름", { 쓰기 가능: 거짓, 구성 가능: 거짓 }); // user.name 또는 해당 플래그를 변경할 수 없습니다. // 이 모든 것은 작동하지 않습니다: user.name = "피트"; 사용자 이름 삭제; Object.defineProperty(user, "name", { value: "Pete" });
유일한 속성 변경 가능: 쓰기 가능 true → false
플래그 변경에 대한 사소한 예외가 있습니다.
구성할 수 없는 속성에 대해 writable: true
false
로 변경하여 값 수정을 방지할 수 있습니다(다른 보호 계층을 추가하기 위해). 하지만 그 반대는 아닙니다.
한 번에 많은 속성을 정의할 수 있는 Object.defineProperties(obj, descriptors) 메서드가 있습니다.
구문은 다음과 같습니다.
Object.defineProperties(obj, { prop1: 설명자1, prop2: 설명자2 // ... });
예를 들어:
Object.defineProperties(사용자, { 이름: { 값: "John", 쓰기 가능: false }, 성: { 값: "Smith", 쓰기 가능: false }, // ... });
따라서 한 번에 많은 속성을 설정할 수 있습니다.
모든 속성 설명자를 한 번에 가져오려면 Object.getOwnPropertyDescriptors(obj) 메서드를 사용할 수 있습니다.
Object.defineProperties
와 함께 객체를 복제하는 "플래그 인식" 방법으로 사용할 수 있습니다.
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
일반적으로 객체를 복제할 때 다음과 같이 할당을 사용하여 속성을 복사합니다.
for (사용자에게 키 입력) { 클론[키] = 사용자[키] }
…하지만 플래그는 복사되지 않습니다. 따라서 "더 나은" 복제본을 원한다면 Object.defineProperties
선호됩니다.
또 다른 차이점은 for..in
기호 및 열거 불가능한 속성을 무시하지만 Object.getOwnPropertyDescriptors
기호 및 열거 불가능한 속성을 포함한 모든 속성 설명자를 반환한다는 것입니다.
속성 설명자는 개별 속성 수준에서 작동합니다.
전체 개체에 대한 액세스를 제한하는 메서드도 있습니다.
Object.preventExtensions(obj)
객체에 새로운 속성을 추가하는 것을 금지합니다.
Object.seal(obj)
속성 추가/제거를 금지합니다. configurable: false
.
객체.동결(obj)
속성의 추가/제거/변경을 금지합니다. 모든 기존 속성에 대해 configurable: false, writable: false
설정합니다.
또한 이에 대한 테스트도 있습니다.
Object.isExtensible(obj)
속성 추가가 금지된 경우 false
반환하고, 그렇지 않으면 true
반환합니다.
Object.isSealed(obj)
속성 추가/제거가 금지되고 모든 기존 속성이 구성 가능하면 true
반환합니다 configurable: false
.
Object.isFrozen(obj)
속성 추가/제거/변경이 금지되고 모든 현재 속성이 구성 가능한 경우 true
반환합니다 configurable: false, writable: false
.
이러한 방법은 실제로는 거의 사용되지 않습니다.