우테코 미션이 끝난 뒤 다른 사람들 코드를 보는데 몇몇분들이 상수를 const
로 선언하면서 Object.freeze()
로 상수를 관리하고 있었습니다.
저는 아래와 같이 선언해서 사용하고 있었는데, 왜 Object.freeze()
를 사용하는지 알아보았습니다.
export const ERROR_PREFIX = '[ERROR]';
export const ERROR = {
participant: `${ERROR_PREFIX} 참가자 입력값을 확인하세요.`,
repeatNumber: `${ERROR_PREFIX} 시도할 횟수 입력값을 확인하세요.`,
};
const
로 선언한 원시타입 상수의 경우 아래처럼 재할당이 불가능 하지만
ERROR_PREFIX = 'HELLO'
// TypeError: Assignment to constant variable.
아래처럼 참조타입인 배열
또는 객체
는 자유롭게 할당된 값을 변경할 수 있습니다.
메모리 주소값에 할당된 주소는 바뀌지 않고 할당된 값만 변경했기 때문이죠.
console.log(ERROR.participant);
// `[ERROR] 참가자 입력값을 확인하세요.`
PROMPT.joinList = "수정";
console.log(ERROR.participant);
// `수정`
이러한 상황에서 Object.freeze()
를 사용하면 위와 같이 불변성에 위반되는 행위를 예방할 수 있습니다.
const ERROR_PREFIX = '[ERROR]';
export const ERROR = Object.freeze({
participant: `${ERROR_PREFIX} 참가자 입력값을 확인하세요.`,
repeatNumber: `${ERROR_PREFIX} 시도할 횟수 입력값을 확인하세요.`,
array : [1, 2, 3]
});
ERROR_PREFIX = 'HELLO';
// TypeError: Assignment to constant variable.
ERROR.participant = '수정';
// TypeError: Cannot assign to read only property 'participant' of object '#<Object>'
그러나 Object.freeze()
는 얕은 동결, 즉 depth 1 까지만 얼려주기 때문에 depth 2 이상의 데이터는 여전히 수정이 가능합니다.
console.log(ERROR.array)
// [1, 2, 3]
ERRPR.array.push(4)
console.log(ERROR.array)
// [1, 2, 3, 4]
불변성을 확보하기 위해 아래 예시와 같이 depth 2 이상의 데이터도 동결하는 함수를 직접 만들어 사용합니다.
function deepFreeze(object) {
Object.keys(object).forEach((key) => {
if (typeof object[key] === "object" && object[key] !== null) {
deepFreeze(object[key]);
}
});
return Object.freeze(object);
}
null 체킹을 하는 이유는 typeof null
의 결과가 object
이기 때문
아래처럼 deepFreeze함수를 이용해 재귀적으로 동결시켜주면 원하던 결과인 depth가 깊은 데이터도 불변하게 유지할 수 있습니다!
export const ERROR = deepFreeze({
participant: `${ERROR_PREFIX} 참가자 입력값을 확인하세요.`,
repeatNumber: `${ERROR_PREFIX} 시도할 횟수 입력값을 확인하세요.`,
array: [1, 2, 3],
});
console.log(ERROR.array);
// [1, 2, 3]
ERROR.array.push(4);
console.log(ERROR.array);
// TypeError: Cannot add property 3, object is not extensible