JavaScript에서 객체의 깊은 복사 기능을 구현하는 데에는 다양한 데이터 타입과 예외 상황을 고려해야 합니다. 특히 Set, Map, Promise 등 특수한 구조를 가진 객체 처리가 중요합니다.
Promises 사용 예시
async function printMessage(message) {
console.log(message);
}
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
await delay(3000).then(() => printMessage("f1"));
await Promise.all([delay(3000), delay(4000)]).then(() => printMessage("f2"));
깊은 복사 알고리즘
const deepCopy = (function() {
'use strict';
function isInstanceOf(obj, type) {
return type != null && obj instanceof type;
}
const NativeMap = typeof Map !== 'undefined' ? Map : () => {};
const NativeSet = typeof Set !== 'undefined' ? Set : () => {};
const NativePromise = typeof Promise !== 'undefined' ? Promise : () => {};
function deepCopy(source, circular = true, depth = Infinity, prototype, includeNonEnumerable) {
if (typeof circular === 'object') {
depth = circular.depth;
prototype = circular.prototype;
includeNonEnumerable = circular.includeNonEnumerable;
circular = circular.circular;
}
const parentStack = [];
const childStack = [];
function _deepCopy(source, depth) {
if (source === null) return null;
if (depth === 0) return source;
let result;
if (isInstanceOf(source, NativeMap)) {
result = new NativeMap();
} else if (isInstanceOf(source, NativeSet)) {
result = new NativeSet();
} else if (isInstanceOf(source, NativePromise)) {
result = new NativePromise((resolve, reject) => {
source.then(value => resolve(_deepCopy(value, depth - 1)),
err => reject(_deepCopy(err, depth - 1)));
});
} else if (Array.isArray(source)) {
result = [];
} else if (isRegExp(source)) {
result = new RegExp(source.source, getRegExpFlags(source));
if (source.lastIndex) result.lastIndex = source.lastIndex;
} else if (isDate(source)) {
result = new Date(source.getTime());
} else if (Buffer && Buffer.isBuffer(source)) {
result = Buffer.from(source);
return result;
} else if (isError(source)) {
result = Object.create(source);
} else {
result = Object.create(prototype || Object.getPrototypeOf(source));
}
if (circular) {
const index = parentStack.indexOf(source);
if (index !== -1) return childStack[index];
parentStack.push(source);
childStack.push(result);
}
if (isInstanceOf(source, NativeMap)) {
source.forEach((value, key) => {
result.set(deepCopy(key, depth - 1), deepCopy(value, depth - 1));
});
} else if (isInstanceOf(source, NativeSet)) {
source.forEach(value => {
result.add(deepCopy(value, depth - 1));
});
}
for (const prop in source) {
const descriptor = Object.getOwnPropertyDescriptor(prototype || Object.getPrototypeOf(source), prop);
if (descriptor && descriptor.set == null) continue;
result[prop] = deepCopy(source[prop], depth - 1);
}
if (Object.getOwnPropertySymbols) {
const symbols = Object.getOwnPropertySymbols(source);
for (const symbol of symbols) {
const desc = Object.getOwnPropertyDescriptor(source, symbol);
if (desc && !desc.enumerable && !includeNonEnumerable) continue;
result[symbol] = deepCopy(source[symbol], depth - 1);
if (!desc.enumerable) Object.defineProperty(result, symbol, { enumerable: false });
}
}
if (includeNonEnumerable) {
const props = Object.getOwnPropertyNames(source);
for (const prop of props) {
const desc = Object.getOwnPropertyDescriptor(source, prop);
if (desc && desc.enumerable) continue;
result[prop] = deepCopy(source[prop], depth - 1);
Object.defineProperty(result, prop, { enumerable: false });
}
}
return result;
}
return _deepCopy(source, depth);
}
function isRegExp(obj) { return typeof obj === 'object' && Object.prototype.toString.call(obj) === '[object RegExp]'; }
function isDate(obj) { return typeof obj === 'object' && Object.prototype.toString.call(obj) === '[object Date]'; }
function isError(obj) { return typeof obj === 'object' && Object.prototype.toString.call(obj) === '[object Error]'; }
function getRegExpFlags(re) { return (re.global ? 'g' : '') + (re.ignoreCase ? 'i' : '') + (re.multiline ? 'm' : ''); }
return deepCopy;
})();
if (typeof module === 'object' && module.exports) {
module.exports = deepCopy;
}
사용 예시
const deepCopy = require('deep-copy');
let original = { nested: { value: 'original' } };
let copy = deepCopy(original);
original.nested.value = 'modified';
console.log(original); // { nested: { value: 'modified' } }
console.log(copy); // { nested: { value: 'original' } }
순환 참조 테스트
let obj = { hello: 'world' };
obj.self = obj;
let copy = deepCopy(obj);
console.log(copy.self); // [Circular]