불변성은 함수형 프로그래밍의 핵심 원칙이며, 객체 지향 프로그램에도 많은 것을 제공합니다. 변경 가능한 객체는 생성된 후 상태를 수정할 수 있는 객체입니다. 변경 불가능한 객체는 생성된 후 상태를 수정할 수 없는 객체입니다.
JavaScript에서 변경 불가능한 객체의 예시는 무엇입니까?
JavaScript에서 일부 내장 타입(숫자, 문자열)은 변경 불가능하지만, 사용자 정의 객체는 일반적으로 변경 가능합니다.
일부 내장 변경 불가능한 JavaScript 객체는 Math, Date입니다.
일반 JavaScript 객체에 불변성을 추가/시뮬레이션하는 몇 가지 방법은 다음과 같습니다.
객체 상수 속성
writable: false와 configurable: false를 결합하여 본질적으로 상수(변경, 재정의 또는 삭제할 수 없음)를 객체 속성으로 만들 수 있습니다. 예를 들어:
let myObject = {};
Object.defineProperty(myObject, 'number', {
value: 42,
writable: false,
configurable: false,
});
console.log(myObject.number); // 42
myObject.number = 43;
console.log(myObject.number); // 42
확장 방지
객체에 새 속성이 추가되는 것을 방지하고 싶지만, 객체의 나머지 속성은 그대로 두려면 Object.preventExtensions(...)를 호출합니다:
var myObject = {
a: 2,
};
Object.preventExtensions(myObject);
myObject.b = 3;
myObject.b; // undefined
비엄격 모드에서는 b 생성이 자동으로 실패합니다. 엄격 모드에서는 TypeError가 발생합니다.
봉인(Seal)
Object.seal()은 '봉인된' 객체를 생성합니다. 이는 기존 객체를 가져와 본질적으로 Object.preventExtensions()를 호출하지만, 모든 기존 속성을 configurable: false로 표시합니다.
따라서 더 이상 속성을 추가할 수 없을 뿐만 아니라 기존 속성을 재구성하거나 삭제할 수도 없습니다(단, 값은 여전히 수정할 수 있음).
고정(Freeze)
Object.freeze()는 고정된 객체를 생성합니다. 이는 기존 객체를 가져와 본질적으로 Object.seal()을 호출하지만, 모든 '데이터 접근자' 속성을 writable:false로 표시하여 해당 값을 변경할 수 없도록 합니다.
이 접근 방식은 객체 자체에 대해 달성할 수 있는 가장 높은 수준의 불변성으로, 객체 또는 직접적인 속성에 대한 변경을 방지합니다(위에서 언급했듯이 참조된 다른 객체의 내용은 영향을 받지 않음).
var immutable = Object.freeze({});
객체를 고정하면 새 속성을 객체에 추가할 수 없으며 기존 속성을 제거하거나 변경하는 것을 방지합니다. Object.freeze()는 객체의 열거 가능성, 구성 가능성, 쓰기 가능성 및 프로토타입을 보존합니다. 전달된 객체를 반환하며 고정된 복사본을 만들지 않습니다.
불변성의 장단점은 무엇입니까?
장점
- 쉬운 변경 감지 - 참조 동일성을 통해 객체 동일성을 성능이 좋고 쉽게 결정할 수 있습니다. 이는 React 및 Redux에서 객체 차이를 비교하는 데 유용합니다.
- 불변 객체가 있는 프로그램은 객체가 시간이 지남에 따라 어떻게 진화할 수 있는지 걱정할 필요가 없으므로 생각하기에 덜 복잡합니다.
- 불변 객체가 함수에서 반환되거나 함수에 전달될 때 방어적 복사본이 더 이상 필요하지 않습니다. 불변 객체가 함수에 의해 수정될 가능성이 없기 때문입니다.
- 참조를 통한 쉬운 공유 - 한 객체의 복사본은 다른 객체만큼 좋으므로 객체를 캐시하거나 동일한 객체를 여러 번 재사용할 수 있습니다.
- 스레드 안전 - 불변 객체는 다른 동시 실행 스레드에서 수정될 위험이 없으므로 다중 스레드 환경에서 스레드 간에 안전하게 사용할 수 있습니다.
- ImmutableJS와 같은 라이브러리를 사용하면 객체가 구조 공유를 사용하여 수정되고 유사한 구조를 가진 여러 객체를 갖는 데 필요한 메모리가 줄어듭니다.
단점
- 불변 데이터 구조 및 해당 작업의 순진한 구현은 새로운 객체가 매번 생성되므로 성능이 매우 저하될 수 있습니다. 구조 공유를 활용하는 효율적인 불변 데이터 구조 및 작업을 위해 라이브러리를 사용하는 것이 좋습니다.
- 기존 객체를 수정하는 대신 많은 작은 객체를 할당(및 할당 해제)하면 성능에 영향을 줄 수 있습니다. 할당자 또는 가비지 컬렉터의 복잡성은 일반적으로 힙의 객체 수에 따라 달라집니다.
- 그래프와 같은 순환 데이터 구조는 구축하기 어렵습니다. 초기화 후 수정할 수 없는 두 객체가 있는 경우 어떻게 서로를 가리키게 할 수 있습니까?
자신의 코드에서 불변성을 어떻게 달성할 수 있습니까?
대안은 const 선언을 위에서 언급한 생성 기술과 결합하는 것입니다. 객체를 '변형'하려면 원본 객체를 변형하는 대신 스프레드 연산자, Object.assign, Array.concat() 등을 사용하여 새 객체를 만듭니다.
예시:
// 배열 예시
const arr = [1, 2, 3];
const newArr = [...arr, 4]; // [1, 2, 3, 4]
// 객체 예시
const human = Object.freeze({ race: 'human' });
const john = { ...human, name: 'John' }; // {race: "human", name: "John"}
const alienJohn = { ...john, race: 'alien' }; // {race: "alien", name: "John"}