한 번 생성된 후 그 상태를 변경할 수 없는 객체.
즉, 객체가 생성된 후 그 내부의 상태(값)를 수정할 수 없고, 새로운 값을 가진 새로운 객체를 생성해야 한다.
참조형 데이터의 가변은 데이터 자체가 아닌 내부 프로퍼티를 변경할 때만 성립한다. 데이터 자체를 변경하고자 하면(새로운 데이터를 할당하고자 하면) 기본형 데이터와 마찬가지로 기존 데이터는 변하지 않는다.
그렇다면, 내부 프로퍼티를 변경할 필요가 있을 때 마다 매번 새로운 객체를 만들어 재할당하기로 규칙을 정하거나 자동으로 새로운 객체를 만드는 도구를 활용한다면 객체 역시 불변성을 확보할 수 있는 것이다!
값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야하는 경우!
var user = {
name: 'Huijeong',
gender: 'female'
};
var changeName = function (user, newName) {
var newUser = user;
newUser.name = newName;
return newUser;
};
var user2 = changeName(user, 'Nam');
if(user !== user2){
console.log('유저 정보가 변경되었습니다.');
}
console.log(user.name, user2.name); // Nam Nam
console.log(user === user2); // true
객체의 가변성으로 인한 문제점을 보여주는 예시.
원본이 변경되어 큰 오류를 낳는다.
해결방법
var user = {
name: 'Huijung',
gender: 'female'
};
var changeName = function (user, newName) {
return {
name: newName,
gender: user.gender
};
};
var user2 = changeName(user, 'Nam');
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.'); // 유저 정보가 변경되었습니다.
}
console.log(user.name, user2.name); // Huijeong Nam
console.log(user === user2); // false
changeName
함수가 새로운 객체를 반환하도록 수정했다. 하지만 새로운 객체를 만들도록 해서 변경할 필요가 없는 프로퍼티(gender
)까지 하드코딩으로 입력했다.
대상 객체에 정보가 많을수록 변경해야 할 정보가 많을수록 사용자가 입력하는 수고가 늘어날 것이다. 이런 방식보단 대상 객체의 프로퍼티 개수에 상관없이 모든 프로퍼티를 복사하는 함수를 만드는 편이 더 좋을 것이다.
let user = {
name: "huijeong",
gender: "female"
};
function copyObject(target) {
let result = {};
for(let prop in target) {
result[prop] = target[prop];
}
return result;
}
let user2 = copyObject(user);
user2.name = 'nam';
if (user !== user2) {
console.log("유저 정보가 변경되었습니다.");
}
console.log(user.name, user2.name); // huijeong nam
console.log(user === user2); // false
copyObject
함수는 for in
문법을 이용해 result
객체에 target
객체의 프로퍼티들을 복사하는 함수이다.
BUT copyObject
함수를 활용해서 객체를 만들었을 때, 가장 아쉬운 점은 얕은 복사만을 수행한다는 점
for...in
for...in
문은 상속된 열거 가능한 속성들을 포함하여 객체에서 문자열로 키가 지정된 모든 열거 가능한 속성에 대해 반복한다. (Symbol로 키가 지정된 속성은 무시.)for...in
반복문은 배열에 사용하지 않는 것for (variable in object) {
// 실행할 코드
}
const person = {
name: 'John',
age: 30,
gender: 'male',
};
for (let prop in person) {
console.log(`${prop}: ${person[prop]}`);
}
Object.assign()
// 배열의 얕은 복사
const originalArray = [1, 2, 3];
const copiedArray = originalArray.slice(); // 또는 [...originalArray];
copiedArray[0] = 99;
console.log(originalArray); // 출력 결과: [1, 2, 3] (원본 배열은 변경되지 않음)
// 객체의 얕은 복사
const originalObj = { name: 'John', age: 30 };
const copiedObj = Object.assign({}, originalObj); // 또는 { ...originalObj };
copiedObj.name = 'Alice';
console.log(originalObj.name); // 출력 결과: John (원본 객체는 변경되지 않음)
BUT
객체의 속성을 순회하면서 중첩된 객체가 있을 경우 해당 객체도 재귀적으로 복사하는 방법.
// 재귀적인 방법으로 깊은 복사 수행
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
let copy;
if (Array.isArray(obj)) {
copy = [];
for (let i = 0; i < obj.length; i++) {
copy.push(deepCopy(obj[i]));
}
} else {
copy = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]);
}
}
}
return copy;
}
// 예시 객체
const originalObj = {
name: 'John',
age: 30,
address: {
city: 'New York',
country: 'USA'
}
};
// 깊은 복사 수행
const copiedObj = deepCopy(originalObj);
// 중첩된 객체 변경
copiedObj.address.city = 'Los Angeles';
// 원본 객체는 변경되지 않음
console.log(originalObj.address.city);
// 출력 결과: New York (원본 객체는 변경되지 않음)
JSON.parse()
와 JSON.stringify()
메서드를 사용하여 객체를 JSON 문자열로 변환한 뒤 다시 객체로 변환하는 방법이다.
function deepCopyWithJSON(obj) {
return JSON.parse(JSON.stringify(obj));
}
// 예시 객체
const originalObj = {
name: 'John',
age: 30,
address: {
city: 'New York',
country: 'USA',
},
};
// 깊은 복사 수행
const copiedObj = deepCopyWithJSON(originalObj);
// 중첩된 객체 변경
copiedObj.address.city = 'Los Angeles';
// 원본 객체는 변경되지 않음
console.log(originalObj.address.city); // 출력 결과: New York
아 너무 잘봤습니다! 좋은 내용 감사합니다!