기존에는 JSON.parse(JSON.stringfy(o)) 방식을 많이 사용했다.
기존 JSON API의 문제
Date 객체가 문자열이 되는 문제
const Person = {
name: "John",
date: new Date("2022-03-25"),
friends: ["Steve", "Karen"]
}
// JSON.stringify `date`를 문자열로 변환합니다.
const buggyCopy = JSON.parse(JSON.stringify(Person))
{
name: 'John',
date: '2022-03-25T00:00:00.000Z',
friends: [ 'Steve', 'Karen' ]
}
console.log(typeof buggyCopy.date); // string
JSON은 객체를 문자열로 인코딩하는 형식이다.
Serialization(직렬화)를 사용하여 객체를 문자열로 변환하고, 역직렬화(deserialization)를 통해 문자열을 객체로 변환한다.
이것이 JSON.stringify가 기본 객체, 배열 및 원시 타입만 다룰 수 있는 이유다.
이는 다른 타입과 함께 작동하는 방법을 예측하기 어렵다.
예를 들어, Date는 string으로 변환되고 Set은 {}로 변환된다.
*직렬화: 객체나 데이터 구조를 네트워크나 저장소(예: 배열 버퍼 또는 파일 형식)를 통한 전송에 적합한 형식으로 변환하는 프로세스
structuredClone API

그러나 structuredClone()에서는 위(JSON)와 같은 문제가 발생하지 않는다.
타입
structuredClone<T = any>(value: T, options?: StructuredSerializeOptions | undefined): T
value
복제된 객체.
options
- transfer : 반환된 객체로 복제되는 대신 이동될 전송가능한 객체 (en-US)의 배열
returns
반환 값은 원래 value의 깊은 복사입니다.
🚨예외
DataCloneError DOMException (en-US)
입력 값의 일부라도 직렬화될 수 없는 경우 예외(DataCloneError)를 던집니다.
코드로 확인하기
const Person = {
name: 'John',
date: new Date('2022-03-25'),
friends: ['Steve', 'Karen'],
};
const bugfreeCopy = structuredClone(Person);
console.log(bugfreeCopy);
// {
// name: 'John',
// date: 2022-03-25T00:00:00.000Z,
// friends: [ 'Steve', 'Karen' ]
// }
console.log(typeof bugfreeCopy.date);

한계(복제 가능한 타입의 한계)
structuredClone()은 JSON.stringify() 메서드의 대부분(모든 것은 아니지만)의 약점을 보완했지만, 몇 가지 주의사항이 있다.
1. 함수는 복사할 수 없다. 함수를 포함하는 객체를 복사하면 DataCloneError가 throw 된다.
structuredClone({ fn: () => {} });
// 🚨 DOMException [DataCloneError]: () => { } could not be cloned.
2. DOM 노드는 복사할 수 없다. DOM 노드를 복제하려고 하면 DataCloneError가 throw 된다.
structuredClone({ element: document.body })
// 🚨Uncaught DOMException: Failed to execute 'structuredClone' on 'Window': HTMLBodyElement object could not be cloned.
3. 프로퍼티 디스크립터(Property descriptors), 세터(setter), 게터(getter)는 복사할 수 없다.
디스크립터는 Object.defineProperty 참고
4. 프로토타입은 복제할 수 없습니다. 구조화된 복제(Structured cloning)는 프로토타입 체인을 복제하지 않습니다. Class의 인스턴스를 복사하면 복사된 객체는 더 이상 해당 Class의 인스턴스가 아닙니다. 원래 Class 대신 일반 객체가 반환됩니다.
class mainClass {
greet = 'hello';
Method() {
/* ... */
}
}
const instanceClass = new mainClass();
const copied = structuredClone(instanceClass);
console.log(copied);
// 결과: { greet: 'hello' }
console.log(copied instanceof mainClass); // false
The structured clone algorithm
)mdn 문서 영문 참고(structured clone이 support 하는 대상 확인용)
The structured clone algorithm copies complex JavaScript objects. It is used internally when invoking structuredClone(), to transfer data between Workers via postMessage(), storing objects with IndexedDB, or copying objects for other APIs.
It clones by recursing through the input object while maintaining a map of previously visited references, to avoid infinitely traversing cycles.
Things that don’t work with structured clone
Functionobjects cannot be duplicated by the structured clone algorithm; attempting to throws aDataCloneErrorexception.- Cloning DOM nodes likewise throws a
DataCloneErrorexception. - Certain object properties are not preserved:
- The
lastIndexproperty ofRegExpobjects is not preserved. - Property descriptors, setters, getters, and similar metadata-like features are not duplicated. For example, if an object is marked readonly with a property descriptor, it will be read/write in the duplicate, since that’s the default.
- The prototype chain is not walked or duplicated.
- The
Supported types
JavaScript types
ArrayArrayBufferBooleanDataViewDateErrortypes (but see Error types below).MapNumberObjectobjects: but only plain objects (e.g. from object literals).- Primitive types, except
symbol. RegExp: but note thatlastIndexis not preserved.SetStringTypedArray
plain object 예시
// plain object
const person = {
name: 'John',
age: 30,
};
// non plain object
const notPlainObject = new Object(); // 생성자 함수를 사용함
Error types
For Error types, the error name must be one of: Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError (or will be set to “Error”).
Browsers must serialize the properties name and message, and are expected to serialize other “interesting” properties of the errors such as stack, cause, etc.
AggregateError support is expected to be added to the specification in whatwg/html#5749 (and is already supported in some browsers).
Web/API types
AudioDataBlobCropTargetCryptoKeyDOMException: browsers must serialize the propertiesnameandmessage. Other attributes may also be serialized/cloned.DOMMatrixDOMMatrixReadOnlyDOMPointDOMPointReadOnlyDOMQuadDOMRectDOMRectReadOnlyFileFileListFileSystemDirectoryHandleFileSystemFileHandleFileSystemHandleGPUCompilationInfoGPUCompilationMessageImageBitmapImageDataRTCCertificateVideoFrame
또 다른 방법 : 깊은 복사(deep copy)
객체의 깊은 복사(deep copy)를 원한다면(모든 프로토타입 체인과 내포된 속성들 까지도), 또다른 접근방식을 사용해야만 한다.(mdn 참고)
function clone(objectToBeCloned) {
// Basis.
if (!(objectToBeCloned instanceof Object)) {
return objectToBeCloned;
}
var objectClone;
// Filter out special objects.
var Constructor = objectToBeCloned.constructor;
switch (Constructor) {
// Implement other special objects here.
case RegExp:
objectClone = new Constructor(objectToBeCloned);
break;
case Date:
objectClone = new Constructor(objectToBeCloned.getTime());
break;
default:
objectClone = new Constructor();
}
// Clone each property.
for (var prop in objectToBeCloned) {
objectClone[prop] = clone(objectToBeCloned[prop]);
}
return objectClone;
}
레퍼런스
- https://soobing.github.io/javascript/deep-copying-objects-with-the-structuredclone-api/
- https://developer.mozilla.org/ko/docs/Web/API/structuredClone
- https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types
이 글은 옵시디언(Obsidian)에서 작성되었습니다. 티스토리에서 상대경로 링크는 작동하지 않습니다. 큰 이미지 파일은 업로드되지 않을 수 있습니다.