객체 타입(Object Type)의 데이터는 변경될 수 있어서 예상치 못한 오류가 발생할 수 있습니다. 다음과 같은 코드를 보면, y의 속성 값을 변경했는데 x의 속성 값도 같이 변경되는 것을 확인할 수 있습니다.
let x = {
size: 21
};
let y = x;
y.size = 25;
console.log(x.size); // 25
만약 코드가 더욱 복잡해진다면, 코드의 결과를 예측하기가 어려워집니다. 이러한 문제점을 해결하기 위해서 크게 2가지의 방법이 존재합니다. 객체 타입의 데이터를 복사할 때 깊은 복사를 하는 방법과, 객체 자체를 불변하게 만드는 것입니다.
① 깊은 복사하기
얕은 복사는 해당 객체만 복사하여 새로운 객체가 같은 메모리 주소를 참조하고 있게 합니다. 반대로 깊은 복사는 해당 객체의 실제 값 전부를 복사하여 새로운 주소를 참조하게 합니다. 자바스크립트에서 깊은 복사는 Object.assign() 메소드와 spread 연산자를 통해서 할 수 있습니다.
1) Object.assign() 메소드
Object.assign() 메소드의 구문은 다음과 같습니다.
Object.assign(target, source)
target 인수는 새롭게 만들어지는 객체이며, source 인수는 복사의 대상이 되는 객체입니다.
let x_copy1 = {
size: 21
};
let y_copy1 = Object.assign({}, x_copy1);
y_copy1.size = 25;
console.log(x_copy1.size); // 21
→ y_copy1의 속성 값을 변경해도 x_copy1의 속성 값은 변경되지 않습니다.
2) spread 연산자
spread 연산자는 복사하고자 하는 객체 앞에 점 세개, ...을 붙이면 됩니다.
let x_copy2 = {
size: 21,
text: {
word: 'apple',
}
};
let y_copy2 = {...x_copy2};
y_copy2.size = 25;
y_copy2.text.word = 'banana';
console.log(x_copy2.size); // 21
console.log(x_copy2.text.word); // 'banana'
사용 방법은 비교적 단순하지만, 깊이가 2단계 이상이라면 깊은 복사가 되지 않습니다. 따라서 2단계 이상의 객체까지 깊은 복사를 하기 위해서라면 spread 연산자를 객체 내부의 객체들까지 사용해주어야 합니다.
② 객체를 불변하게 만들기
객체를 복사할 때 깊은 복사를 해서 새로운 객체를 만들 수 있지만, 아예 객체 자체를 불변하게 만드는 방법이 있습니다.
총 3가지 메소드 Object.preventExtensions(), Object.seal(), Object.freeze()를 통해서 객체의 변경을 막을 수 있습니다. 각 메소드들은 불변성을 어느정도까지 유지하는지에 따라 차이가 있습니다. 먼저 결론적으로 말하면
preventExtensions < seal < freeze
순서로 불변성이 강하게 유지됩니다. 그러면 각 메소드들에 대해서 자세하게 설명드리도록 하겠습니다.
1) Object.preventExtensions() 메소드
Object.preventExtensions() 메소드는 이름으로 유추할 수 있듯이, 객체의 확장을 막는 메소드입니다. 새로운 속성을 추가하려고 시도한다면, TypeError가 발생하거나 조용히 실패하게 됩니다. 새로운 속성을 추가하지는 못하지만, 기존의 속성 값을 변경하거나 delete 연산자를 통해서 속성을 삭제할 수는 있습니다.
const coordinate1 = {
x: 10,
y: 20
};
Object.preventExtensions(coordinate1);
Object.defineProperty(coordinate1, "z", {
value: 30,
}); // TypeError occurs
console.log(coordinate1); // {x: 10, y: 20}
Object.defineProperty(coordinate1, 'x', {
value: 15,
});
console.log(coordinate1); // {x: 15, y: 20}
delete coordinate1.y;
console.log(coordinate1); // {x: 15}
2) Object.seal() 메소드
Object.seal() 메소드는 속성의 추가와 속성의 삭제를 둘 다 막는 메소드입니다. 단, 기존의 속성 값을 변경하는 것은 가능합니다.
const coordinate2 = {
x: 40,
y: 50
};
Object.seal(coordinate2);
Object.defineProperty(coordinate2, "z", {
value: 60,
}); // TypeError(silent fail) occurs
console.log(coordinate2); // {x: 40, y: 50}
Object.defineProperty(coordinate2, 'x', {
value: 45,
});
console.log(coordinate2); // {x: 45, y: 50}
delete coordinate2.y; // TypeError(silent fail) occurs
console.log(coordinate2); // {x: 45, y: 50}
3) Object.freeze() 메소드
Object.freeze()를 메소드는 속성의 추가, 속성의 삭제, 속성 값 변경을 모두 막는 강력한 메소드입니다.
const coordinate3 = {
x: 70,
y: 80
};
Object.freeze(coordinate3);
Object.defineProperty(coordinate3, "z", {
value: 90,
}); // TypeError(silent fail) occurs
console.log(coordinate3); // {x: 70, y: 80}
Object.defineProperty(coordinate3, 'x', {
value: 75,
}); // TypeError(silent fail) occurs
console.log(coordinate3); // {x: 70, y: 80}
delete coordinate3.y; // TypeError(silent fail) occurs
console.log(coordinate3); // {x: 70, y: 80}
주의해야 할 점은, Object.freeze() 메소드를 사용했다더라도 2단계 이상의 객체에는 적용이 되지 않는다는 것입니다.
* Object.isSealed() 메소드는 객체에 Object.seal() 메소드가 적용되어 있는지 확인할 때 사용할 수 있습니다. 만약 Object.freeze() 메소드가 적용되어 있다면, freeze의 효과가 seal을 포함하고 있기 때문에 true를 반환하게 됩니다.
Object.isSealed(coordinate1); // false
Object.isSealed(coordinate2); // true
Object.isSealed(coordinate3); // true
** Object.isFrozen() 메소드는 객체에 Object.freeze() 메소드가 적용되어 있는지 확인할 때 사용할 수 있습니다.
Object.isFrozen(coordinate1); // false
Object.isFrozen(coordinate2); // false
Object.isFrozen(coordinate3); // true
'Frontend > Javascript' 카테고리의 다른 글
[JavaScript] toUpperCase(), toLowerCase() 메서드 (0) | 2023.06.27 |
---|---|
[JavaScript] \을 사용한 이스케이프 시퀀스 (0) | 2023.06.26 |
[JavaScript] 불변성(Immutability) vs 가변성(Mutability) (0) | 2023.06.24 |
[JavaScript] =, ==, === 차이 (0) | 2023.06.22 |
[JavaScript] var, let, const 키워드 차이 (0) | 2023.06.20 |