불변 객체는 내부 상태가 변하지 않는 객체를 의미합니다.
프로그래밍에서는 특히 함수형 프로그래밍, 라이브러리/프레임워크, 디자인 패턴 등에서 불변 객체 개념이 자주 등장합니다.
기본형 데이터는 값을 변경할 수 없습니다.
참조형 데이터도 프로퍼티를 바꾸지 않으면 변경된 것이 아닙니다.
어떤 함수를 통해 객체를 전달받고 변경을 하더라도, 원본 객체는 절대로 변경되지 않아야 할 때가 많습니다.
예제:
// 객체의 가변성에 따른 문제
var user = {
name: 'Jaenam',
gender: 'male'
};
var changeName = function (user, newName) {
var newUser = user;
newUser.name = newName;
return newUser;
};
var user2 = changeName(user, 'Jung');
console.log(user.name, user2.name); // Jung Jung
console.log(user === user2); // true (같은 객체)
user2
가 바뀌면 user
도 바뀜 → 두 객체는 같은 참조값을 가지므로 불변성이 깨짐// 새로운 객체를 만들어 반환
var changeName = function (user, newName) {
return {
name: newName,
gender: user.gender
};
};
var user2 = changeName(user, 'Jung');
console.log(user.name, user2.name); // Jaenam Jung
console.log(user === user2); // false
🔺 하지만 이 방식은 객체가 커질수록 하드코딩해야 할 코드가 많아짐 → 비효율적
복사 방식 | 설명 |
---|---|
얕은 복사 (Shallow Copy) | 1단계 값만 복사. 중첩 객체는 주소(참조)만 복사됨 |
깊은 복사 (Deep Copy) | 중첩된 내부 값까지 전부 재귀적으로 복사 |
var copyObject = function (target) {
var result = {};
for (var prop in target) {
result[prop] = target[prop];
}
return result;
};
📌 얕은 복사는 참조형 데이터(객체, 배열 등)의 경우 동일한 메모리 주소를 참조하므로, 내부 프로퍼티가 바뀌면 원본도 영향 받음
var copyObjectDeep = function (target) {
var result = {};
if (typeof target === 'object' && target !== null) {
for (var prop in target) {
result[prop] = copyObjectDeep(target[prop]); // 재귀
}
} else {
result = target;
}
return result;
};
typeof null === 'object'
이기 때문에, null은 따로 필터링 필요hasOwnProperty
를 활용하면 프로토타입에서 상속된 프로퍼티는 제외 가능// ES6 이상에서 getter/setter까지 복사하려면
Object.getOwnPropertyDescriptor(obj, prop);
Object.getOwnPropertyDescriptors(obj);
const deepCopied = JSON.parse(JSON.stringify(obj));
undefined
, 함수, Symbol, __proto__
등은 복사되지 않음자바스크립트에서 "없음"을 표현하는 두 가지:
값 | 설명 |
---|---|
undefined | 자바스크립트가 자동으로 부여하거나, 명시적으로 지정 가능 |
null | 개발자가 명시적으로 설정하는 '값이 없음' |
let x;
console.log(x); // undefined
const obj = {};
console.log(obj.name); // undefined
function noReturn() {}
console.log(noReturn()); // undefined
const arr1 = [1, undefined, 3];
const arr2 = [1, , 3];
arr1.forEach((v, i) => console.log(i, v));
// 0 1
// 1 undefined
// 2 3
arr2.forEach((v, i) => console.log(i, v));
// 0 1
// 2 3 → index 1은 순회되지 않음
즉, 명시적 undefined는 실존,
비어있는 요소는 순회 대상 아님
undefined
는 엔진이 자동으로 줄 수 있으며, 존재하지 않는 값을 의미null
은 개발자가 의도적으로 비워두는 값