JavaScript의 객체를 깊이 복제하는 가장 효율적인 방법은 무엇입니까?
질문
JavaScript 객체를 복제하는 가장 효율적인 방법은 무엇입니까?나는 obj = eval (overal (o))을 보았습니다.사용 중이지만 비표준이 아니며 Firefox가 지원합니다.나는 obj = json.parse (json.stringify (o)와 같은 일을 해왔습니다.그러나 효율성에 대해 질문하십시오.나는 또한 다양한 결함으로 재귀 복사 기능을 보았습니다. 나는 정식 해결책이 존재하지 않아 놀랐다.
답변
네이티브 깊은 복제
이제 노드 11 이상에서 실험적으로 작동하는 "구조화 된 복제"라는 JS 표준이 있습니다. 브라우저에 착륙 할 것이며 기존 시스템에 대한 폴리필이 있습니다.
structuredClone(value)
필요한 경우 먼저 폴리 필 빌로드 :
import structuredClone from '@ungap/structured-clone';
자세한 내용은이 답변을 참조하십시오.
이전 답변
데이터 손실로 빠르게 복제 - json.parse / stringify.
날짜, 함수, 정의되지 않은, 무한, 정규식,지도, 세트, 얼룩, 필립리스트, imageDatas, 스파 스 배열, 객체 내에서 매우 간단한 하나의 라이너가 객체에서 매우 간단한 하나의 라이너를 사용하지 않는 경우 다음과 같습니다.
json.parse (JSON.Stringify (객체))
const a = { 문자열 : '문자열', 번호 : 123, bool : 거짓, NUL : NULL, 날짜 : 새 날짜 (), // stringified. Undef : 정의되지 않은 // 분실 Inf : 무한대, // 'null' 다시 : /.*/, // 분실 } console.log (a); console.log (유형 a.date);// 날짜 객체 const clone = json.parse (json.stringify (a)); console.log (복제); console.log (clone.date type);// .TOISOSTRING ()의 결과
벤치 마크에 대한 Corban의 답변을 참조하십시오.
라이브러리를 사용하여 신뢰할 수있는 복제
클로닝 객체는 사소한 (복잡한 유형, 원형 참조, 기능 등)이 아니므로 대부분의 주요 라이브러리는 오브젝트를 복제하는 기능을 제공합니다.바퀴를 재발견하지 마십시오. 이미 라이브러리를 사용하는 경우 오브젝트 복제 기능이 있는지 확인하십시오.예를 들어,
Lodash - clonedeep;Lodash.clonedeep 모듈을 통해 별도로 가져올 수 있으며 아직 깊은 복제 기능을 제공하는 라이브러리를 사용하지 않는 경우 최선의 선택 일 것입니다. Angularjs - Angular.Copy. jquery - jquery.extend (true, {}, OldObject);.clone () 만 클론 DOM 요소 만 그냥 라이브러리 - 그냥 클론;한 가지 일을하는 제로 의존성 NPM 모듈의 라이브러리의 일부입니다. 모든 경우에 대한 죄책감없는 유틸리티.
ES6 (얕은 복사본)
완전성을 위해 ES6은 Object.Assign () 및 확산 구문의 두 가지 얕은 복사 메커니즘을 제공합니다. 한 개체에서 다른 개체로 모든 열거 형 자체 속성의 값을 복사합니다.예를 들어:
var A1 = {a: "2"};
var A2 = Object.assign({}, A1);
var A3 = {...A1}; // Spread Syntax
답변
체크 아웃이 벤치 마크 : http://jsben.ch/#/bwfk9.
속도가 발견 된 내 이전 테스트에서
JSON.parse(JSON.stringify(obj))
깊은 복제 된 객체 (jquery보다 느리게 가늘어지는 가장 느린 방법이됩니다.
딥 플래그가 False (얕은 복제본)로 설정되면 jquery.extend가 꽤 빨리됩니다.예정성 검증을위한 추가 논리가 포함되므로 정의되지 않은 속성 등을 복사하지 않으므로 조금 느릴 것입니다.
복제하려는 오브젝트의 구조를 알고있는 경우 깊은 중첩 된 배열을 피할 수있는 객체의 구조를 알고있는 경우 hasownproperty를 검사하는 동안 객체를 복제하면서 jQuery보다 훨씬 빨리 훨씬 빠를 수 있습니다.
마지막으로 핫 루프에서 알려진 객체 구조를 복제하려고 시도하는 경우 복제 절차를 단순히 인라인으로 인라인하고 수동으로 개체를 구성하여 훨씬 더 많은 성능을 얻을 수 있습니다.
JavaScript 추적 엔진은 루프를 최적화하고 HasownProperty를 검사하는 것을 늘리고 있습니다.속도가 절대적 인 경우 수동 클론입니다.
var clonedObject = {
knownProp: obj.knownProp,
..
}
날짜 개체의 json.parse (json.stringify (obj)) 메소드를 사용하여주의하십시오. json.stringify (새 날짜 ()) json.parse ()가 다시 변환되지 않는 ISO 형식의 날짜의 문자열 표현을 반환합니다.날짜 객체에.자세한 내용은이 답변을 참조하십시오.
또한 크롬 65에서 최근의 클로닝은가는 길은 아닙니다.JSPERF에 따르면 새로운 기능을 생성하여 원시 복제를 수행하는 것은 json.stringify를 사용하는 것보다 거의 800 배 더 느리게합니다. 이는 믿을 수 없을만큼 믿을 수 없을 정도로 빠릅니다.
ES6 용 업데이트
JavaScript ES6을 사용하는 경우 복제 또는 얕은 복사본을 위해이 기본 방법을 사용해보십시오.
Object.assign({}, obj);
답변
구조화 된 복제
2022 업데이트 : StructuredClone 전역 함수는 이미 Firefox 94, 노드 17 및 Deno 1.14에서 사용할 수 있습니다.
HTML 표준에는 객체의 딥 클론을 만들 수있는 내부 구조화 된 복제 / 직렬화 알고리즘이 포함되어 있습니다.그것은 여전히 특정 내장형 유형으로 제한되지만 JSON이 지원하는 몇 가지 유형 이외에도 날짜, 정규 표현식,지도, 세트, 얼룩, 철피리스트, imagedatas, 스파 스 배열, 입력 된 배열 및 미래에 더 많은 것을 지원합니다....에또한 복제 된 데이터 내의 참조를 보존하여 JSON에 오류를 발생시키는 순환 및 재귀 구조를 지원할 수 있습니다.
node.js에서 지원 :
StructuredClone 전역 함수는 노드 17.0에서 제공합니다.
const clone = structuredClone(original);
이전 버전 : node.js의 v8 모듈 (노드 11)은 구조화 된 직렬화 API를 직접 노출하지만이 기능은 여전히 "실험"으로 표시되어 있으며 향후 버전에서 변경 또는 제거 될 수 있습니다.호환 가능한 버전을 사용하는 경우 객체를 복제하는 것은 다음과 같이 간단합니다.
const v8 = require('v8');
const structuredClone = obj => {
return v8.deserialize(v8.serialize(obj));
};
브라우저에서 직접 지원 : Firefox 94에서 사용할 수 있습니다
StructuredClone 전역 함수는 곧 모든 주요 브라우저 (이전에 GitHub의 Whatwg / HTML # 793에서 논의 된 것)에서 제공됩니다.그것은 이처럼 보입니다 :
const clone = structuredClone(original);
이것이 배송 될 때까지 브라우저의 구조화 된 복제 구현은 간접적으로 만 노출됩니다.
비동기 해결 방법 : 사용 가능.😕.
기존 API가있는 구조화 된 클론을 생성하는 하위 오버 헤드 방식은 MessageChannels의 한 포트를 통해 데이터를 게시하는 것입니다.다른 포트는 첨부 된 .Data의 구조화 된 복제본으로 메시지 이벤트를 방출합니다.불행히도 이러한 이벤트를 듣는 것은 반드시 비동기식이며 동기식 대안은 덜 실용적입니다.
class StructuredCloner {
constructor() {
this.pendingClones_ = new Map();
this.nextKey_ = 0;
const channel = new MessageChannel();
this.inPort_ = channel.port1;
this.outPort_ = channel.port2;
this.outPort_.onmessage = ({data: {key, value}}) => {
const resolve = this.pendingClones_.get(key);
resolve(value);
this.pendingClones_.delete(key);
};
this.outPort_.start();
}
cloneAsync(value) {
return new Promise(resolve => {
const key = this.nextKey_++;
this.pendingClones_.set(key, resolve);
this.inPort_.postMessage({key, value});
});
}
}
const structuredCloneAsync = window.structuredCloneAsync =
StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);
예제 사용 :
const main = async () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = await structuredCloneAsync(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
main();
동기 해결 방법 : 끔찍한!🤢.
구조화 된 클론을 동기식으로 만들기위한 좋은 옵션은 없습니다.대신 실용적인 해킹이 있습니다.
history.pushstate () 및 history.ReplaceState ()는 두 번째 인수의 구조화 된 복제본을 만들고 해당 값을 History.State에 할당합니다.이 옵션을 사용하여 다음과 같이 모든 객체의 구조화 된 복제본을 만듭니다.
const structuredClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const clonedObj = history.state;
history.replaceState(oldState, null);
return clonedObj;
};
예제 사용 :
'엄격한 사용'; const main = () => { const 원본 = {날짜 : 새 날짜 (), 숫자 : math.random ()}; 원본 = 원본; const clone = structuredclone (원본); // 서로 다른 객체입니다. console.assert (원본! == 복제); console.assert (Original.date! == clone.date); // 그들은 순환 : console.assert (원본 === 원본); console.assert (clone.self == clone); // 이에 해당하는 값을 포함합니다. console.assert (Original.number === clone.number); console.assert (number (original.date) == 숫자 (clone.date); console.log ( "어설 션 완료"); }; Const structuredClone = obj => { const oldstate = 히스토리. 스테이트; History.Replacestate (obj, null); const clonedobj = 히스토리. 스테이트; 히스토리. 재판매 (oldstate, null); clonedobj를 반환합니다; }; 기본();
동기식이지만 이는 매우 느려질 수 있습니다.브라우저 기록을 조작하는 것과 관련된 모든 오버 헤드가 발생합니다.이 방법을 반복해서 반복적으로 호출하면 크롬이 일시적으로 응답하지 않을 수 있습니다.
알림 생성자는 관련 데이터의 구조화 된 복제본을 생성합니다.또한 사용자에게 브라우저 알림을 표시하려고 시도하지만 알림 권한을 요청하지 않으면 자동으로 실패합니다.다른 목적으로 허가를받을 경우 즉시 우리가 만든 알림을 즉시 닫을 것입니다.
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.onshow = n.close.bind(n);
return n.data;
};
예제 사용 :
'엄격한 사용'; const main = () => { const 원본 = {날짜 : 새 날짜 (), 숫자 : math.random ()}; 원본 = 원본; const clone = structuredclone (원본); // 서로 다른 객체입니다. console.assert (원본! == 복제); console.assert (Original.date! == clone.date); // 그들은 순환 : console.assert (원본 === 원본); console.assert (clone.self == clone); // 이에 해당하는 값을 포함합니다. console.assert (Original.number === clone.number); console.assert (number (original.date) == 숫자 (clone.date); console.log ( "어설 션 완료"); }; Const structuredClone = obj => { const n = new notification ( '', {데이터 : obj, silent : true}); n.close (); n.Data를 반환합니다. }; 기본();
답변
객체의 함수가 아니라 속성 만 있지만 다음을 사용할 수 있습니다.
var newObject = JSON.parse(JSON.stringify(oldObject));
답변
내장 된 것도 없으면 다음을 시도 할 수 있습니다.
function clone(obj) {
if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
return obj;
if (obj instanceof Date)
var temp = new obj.constructor(); //or new Date(obj);
else
var temp = obj.constructor();
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
obj['isActiveClone'] = null;
temp[key] = clone(obj[key]);
delete obj['isActiveClone'];
}
}
return temp;
}
답변
한 줄의 코드에서 객체를 복제하는 효율적인 방법
Object.Assign 메서드는 ECMAScript 2015 (ES6) 표준의 일부이며 필요한 것을 정확하게 수행합니다.
var clone = Object.assign({}, obj);
Object.Assign () 메서드는 하나 이상의 소스 오브젝트의 모든 열거 형 자체 속성의 값을 대상 개체로 복사하는 데 사용됩니다.
자세히보기 ...
이전 브라우저를 지원하는 폴리 필링 :
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(nextSource);
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
출처:https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript
최근댓글