앞서 살펴본 Object 빌트인 객체의 메서드를 이용해 프로퍼티 상태(attribute)를 제어하는 것이 가능했습니다. 이러한 메서드들의 역할은 사용자에 의해 객체의 프로퍼티들이 변경 됨으로, 자칫 해쳐질 수도 있는 해당 프로퍼티들의 무결성과 보안성을 예방하고자 하는 목적이 큰데요.
그러나 앞서 소개한 Object 빌트인 객체의 defineProperty(ties) 메서드로 해당 프로퍼티들의 어트리뷰트를 제어하는 일은 번거롭고 가시성이 떨어지는 작업이기에 이번 시간에는 이들 메서드 말고도 프로퍼티의 어트리뷰트를 변경하게 해주는 메서드들을 알아보고자 합니다.
이 메서드는 객체의 프로퍼티 추가에 의한 확장을 금지합니다. 즉 이 메서드를 사용시 동적으로 프로퍼티를 추가하는 방법(객체 리터럴 내 직접 입력)과 앞서 살펴본 Object.defineProperty, Object.defineProperties를 통한 추가의 행위가 금지된다는 것입니다.
하지만 단순히 새로운 프로퍼티를 추가하는 행위를 금지할 뿐이지 기존의 프로퍼티에 대한 읽기, 재정의, 삭제 등의 행위와 어트리뷰트 재정의 행위는 가능합니다.
참고로 해당 객체의 확장이 금지되었는지는 Object.isExtensible 메서드를 통해 확인이 가능합니다.
const person = {name : "Re_Go"} //객체 선언 후 Object.preventExtensions(person) // 해당 메서드를 사용해 추가 행위 금지 person.age = 20; // 동적으로 프로퍼티를 추가하는 것도 무시되고 Object.defineProperty(person, "age", { value: 30, writable: true, enumerable: true, configurable: true }, ) // define 류의 메서드를 이용해 프로퍼티를 추가하는 행위 또한 금지됩니다.
이 메서드는 객체의 프로퍼티 어트리뷰트 중 검색을 포함한 읽기(readOnly)와 값 재설정(Writable)만 가능해지고, 이외의 추가나 삭제 속성은 불허합니다.
또한 이 메서드는 define 메서드로 새로운 프로퍼티를 추가하는 것 뿐만 아니라 객체에 존재하는 기존의 프로퍼티에 대한 어트리뷰트에 대한 변경까지 불가능하게 한다는 점도 가지고 있습니다.
참고로 해당 객체가 밀봉 되었는지는 Object.isSealed 메서드를 통해 확인이 가능합니다.
const person = { name: "Re_Go", age: 30 }; > Object.seal(person); //객체 밀봉 > // 프로퍼티 수정(쓰기) 및 읽기는 가능 person.age = 31; console.log(person.age); // 31 > // 새로운 프로퍼티 추가는 불가능 person.job = "job_Seeker"; // 에러 발생 > // 프로퍼티 삭제도 불가능 delete person.name; // 에러 발생 > // 프로퍼티 어트리뷰트 변경은 불가능 Object.defineProperty(person, "age", { writable: false }); // 에러 발생
이 메서드는 객체를 말 그대로 객체를 얼려버리는데, 읽기를 제외한 모든 제어가 불가능하며(readOnly) Object.isFrozen 메서드를 통해 객체의 동결 상태 여부를 확인할 수 있습니다.
const person = { name: "Re_Go", age: 30 }; Object.freeze(person); // 프로퍼티 읽기 (가능) console.log(person.name); // "Re_Go" console.log(person.age); // 30 // 프로퍼티 수정 시도 (불가능) person.name = "Alice"; // 에러 발생 person.age = 31; // 에러 발생 // 새로운 프로퍼티 추가 시도 (불가능) person.job = "Programmer"; // 에러 발생 // 프로퍼티 삭제 시도 (불가능) delete person.name; // 에러 발생 // 프로퍼티 어트리뷰트 변경 시도 (불가능) Object.defineProperty(person, "age", { writable: true }); // 에러 발생
앞서 소개한 각각의 메서드들은 객체의 프로퍼티 제한 정도에 따라 preventExtensions(신규 프로퍼티 제한)->seal(기존 프로퍼티 제한)->freeze(기존 객체에 대한 읽기만 가능) 순으로 나뉘어지는데,
이들 객체들에 대한 각각의 메서드들의 효과는 단일 대상 적용에 제한됩니다. 즉 이중 객체라고 한다면 상위 객체에 대한 해당 메서드들을 호출한다고 하더라도 상위 객체가 감싸고 있는 하위 객체(이중 객체 상황)에 까지는 영향을 주지 않는다는 것입니다.
그래서 만약 이중 객체에 대해서 전부를 메서드로 접근 제어에 제한을 주고 싶을때는 닷 노테이션(.)을 사용해 일일이 접근을 제한하든지, 특정 객체를 가진 키(이중 객체)에 다시 재귀적으로 메서드를 적용시키는 재귀 함수를 구현하는 방법으로 이중 객체에 대한 해당 메서드들을 적용할 수 있습니다.
const person = { // 객체 생성 name: "Re_Go", age: 30, address: { city: "Seoul", zip: "1009" } }; // ① 해당 객체와 이중 객체들을 freeze 메서드로 일일이 동결하는 예시 Object.freeze(person); // 상위 객체 동결 Object.freeze(person.address); // 하위 객체 동결 person.name = "Parkjongmin"; // 에러 발생 person.address.city = "heaven"; // 에러 발생 // ② 해당 객체와 이중 객체들을 freeze 메서드를 포함한 재귀 함수로 동결하는 예시) function deepFreeze(obj) { // 객체를 동결 Object.freeze(obj); // 객체의 모든 프로퍼티에 대해 재귀적으로 동결 수행 for (let key in obj) { //for문을 돌려 전달 받은 객체의 프로퍼티들 중 if (obj.hasOwnProperty(key) && typeof obj[key] === "object") { // 키가 프로퍼티를 가지고 있는 경우(즉 키가 객체라는 의미), 그리고 해당 객체의 키의 타입이 객체(Object)인 경우의 조건을 세워 키 중에 객체(이중 객체)가 있는 경우에는 deepFreeze(obj[key]); // deepFreeze 함수를 재호출하고 인자로 현재 객체의 프로퍼티 중에 객체인 키를 전달하여 그 키의 값까지 동결시킵니다. } } } deepFreeze(person); // 모든 레벨의 객체가 동결되었으므로 변경이 불가능 person.name = "Parkjongmin"; // 에러 발생 person.address.city = "heaven"; // 에러 발생