📌 원시타입 (primitive data type)
재할당 이외의 원시 값인 변수 값을 변경할 수 있다면 예기치않게 변수값이 변경될 수 있다는 것이고,
이는 값의 변경, 즉 상태 변경을 추적하기 어렵게 만든다.
따라서예상가능하고 신뢰할 수 있는 코드를 위해 원시 값은 불변
📌 객체타입 (primitive data type)
예기치 못한 변경이 발생하지 않도록 바로 값을 담는것이 아닌, 메모리 공간 주소를 담아 한 단계 더 거쳐 실제 객체에 접근함으로써 명확하고 신뢰성이 확보된다.
변경 불가능하다는 것은 '변수'가 아닌 '값'이다.
(String)
var statement = 'I am an immutable value';
var otherStr = statement.slice(8, 17);
console.log(otherStr); // 'immutable'
console.log(statement); // 'I am an immutable value'
---------------------------------------------
(Array)
var arr = [];
console.log(arr.length); // 0
var v2 = arr.push(2); // arr.push()는 메소드 실행 후 arr의 length를 반환
console.log(arr.length); // 1
---------------------------------------------
var user1 = {
name: 'Lee',
address: {
city: 'Seoul'
}
};
var user2 = user1; // 변수 user2는 객체 타입이다.
user2.name = 'Kim';
console.log(user1.name); // Kim
console.log(user2.name); // Kim
✔️ 별도로 새로운 메모리에 할당 되는 것이 아니라 같은 곳을 바라보기 때문에 동시에 변경되고 의도된 코딩이 아니라면 막아야한다.
Object.assign(target, ...sources)
const user1 = {
name: 'Lee',
address: {
city: 'Seoul'
}
};
// 새로운 빈 객체에 user1을 copy한다.
const user2 = Object.assign({}, user1);
// user1과 user2는 참조값이 다르다.
console.log(user1 === user2); // false
user2.name = 'Kim';
console.log(user1.name); // Lee
console.log(user2.name); // Kim
// 객체 내부의 객체(Nested Object)는 Shallow copy된다.
console.log(user1.address === user2.address); // true
user1.address.city = 'Busan';
console.log(user1.address.city); // Busan
console.log(user2.address.city); // Busan
/*
user2가 할당한 객체가 수정되어도 user1의 참조 객체는 수정되지 않는다.
user1이 참조한 객체를 수정하면 user2에 할당된 객체도 수정된다.
*/
Object.freeze(obj)
const user1 = {
name: 'Lee',
address: {
city: 'Seoul'
}
};
// Object.assign은 완전한 deep copy를 지원하지 않는다.
const user2 = Object.assign({}, user1, {name: 'Kim'});
console.log(user1.name); // Lee
console.log(user2.name); // Kim
Object.freeze(user1); //프로퍼티 변경 불가능
user1.name = 'Kim'; // 무시된다!
user.adress.city = 'Busan'; // 변경된다!
console.log(user1); // { name: 'Lee', address: { city: 'Seoul' } }
console.log(Object.isFrozen(user1)); // true
---------------------------------------------------------
(객체 내부의 객체(Nested Object)는 변경가능)
const user = {
name: 'Lee',
address: {
city: 'Seoul'
}
};
Object.freeze(user);
user.address.city = 'Busan'; // 변경된다!
console.log(user); // { name: 'Lee', address: { city: 'Busan' } }
---------------------------------------------------------
(Nested Object에 대해서는 하나하나 중첩 안의 객체들에 대해서 freeze로 처리해 주어야 한다. Deep freeze)
function deepFreeze(object) {
//Object.getOwnPropertyNames() 메서드:객체에 정의된 속성명을 추출하여 배열로 반환한다
let props = Object.getOwnPropertyNames(object); //객체 key를 props에 저장
// 스스로를 동결하기 전에 속성을 동결
for(let name of props){ //배열의 length만큼 반복
let value=object[name];
object[name]=value&&typeof value ==="object"?
deppFreeze(value):value;
}
return Object.freeze(object);
}
const user = {
name: 'Lee',
address: {
city: 'Seoul'
}
};
deepFreeze(user);
user.name = 'Kim'; // 무시된다
user.address.city = 'Busan'; // 무시된다
console.log(user); // { name: 'Lee', address: { city: 'Seoul' } }
/*
user2가 할당한 객체가 수정되어도 user1의 참조 객체는 수정되지 않는다.
user1이 참조한 객체를 수정하면 user2에 할당된 객체도 수정된다.
*/
Object.assign과 Object.freeze을 사용하여 불변 객체를 만드는 방법은 번거러울 뿐더러 성능상 이슈가 있어서 큰 객체에는 사용하지 않는 것이 좋다.
또 다른 대안으로 Facebook이 제공하는 Immutable.js를 사용하는 방법이 있다.