etc./StackOverFlow

JavaScript에서 객체를 딥 클론하는 가장 효율적인 방법은 무엇입니까?

청렴결백한 만능 재주꾼 2021. 9. 22. 13:27
반응형

질문자 :Community Wiki


JavaScript 객체를 복제하는 가장 효율적인 방법은 무엇입니까? 나는 obj = eval(uneval(o)); 사용 중이지만 이는 비표준이며 Firefox에서만 지원됩니다 .

나는 obj = JSON.parse(JSON.stringify(o)); 그러나 효율성에 의문을 제기하십시오.

또한 다양한 결함이 있는 재귀 복사 기능을 보았습니다.
정식 솔루션이 존재하지 않는다는 사실에 놀랐습니다.



답변자 : Community Wiki


네이티브 딥 클로닝

"구조화된 복제"라고 하며 Node 11 이상에서 실험적으로 작동하며 브라우저에 적용되기를 바랍니다. 자세한 내용은 이 답변 을 참조하세요.

데이터 손실이 있는 빠른 복제 - JSON.parse/stringify

Date s, functions, undefined , Infinity , RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays 또는 다른 복잡한 유형을 개체 내에서 사용하지 않는 경우 개체를 심층 복제하는 매우 간단한 하나의 라이너는 다음과 같습니다. :

JSON.parse(JSON.stringify(object))

 const a = { string: 'string', number: 123, bool: false, nul: null, date: new Date(), // stringified undef: undefined, // lost inf: Infinity, // forced to 'null' re: /.*/, // lost } console.log(a); console.log(typeof a.date); // Date object const clone = JSON.parse(JSON.stringify(a)); console.log(clone); console.log(typeof clone.date); // result of .toISOString()

벤치마크는 Corban의 답변 을 참조하십시오.

라이브러리를 사용한 안정적인 복제

객체 복제는 사소하지 않기 때문에(복잡한 유형, 순환 참조, 함수 등) 대부분의 주요 라이브러리는 객체 복제 기능을 제공합니다. 바퀴를 재발명하지 마십시오. 이미 라이브러리를 사용하고 있다면 개체 복제 기능이 있는지 확인하십시오. 예를 들어,

  • lodash - cloneDeep ; lodash.clonedeep 모듈을 통해 별도로 가져올 수 있으며 딥 복제 기능을 제공하는 라이브러리를 아직 사용하지 않는 경우 가장 좋은 선택일 것입니다.
  • AngularJS - angular.copy
  • jQuery - jQuery.extend(true, { }, oldObject) ; .clone() 은 DOM 요소만 복제합니다.
  • 그냥 라이브러리 - just-clone ; 한 가지만 수행하는 제로 종속성 npm 모듈 라이브러리의 일부입니다. 모든 경우에 사용할 수 있는 무죄 유틸리티.

ES6( 얕은 카피)

Object.assign()스프레드 구문 이라는 두 가지 얕은 복사 메커니즘을 제공합니다. 하나의 개체에서 다른 개체로 모든 열거 가능한 자체 속성의 값을 복사합니다. 예를 들어:

 var A1 = {a: "2"}; var A2 = Object.assign({}, A1); var A3 = {...A1}; // Spread Syntax


답변자 : Community Wiki


이 벤치마크를 확인하세요: http://jsben.ch/#/bWfk9

속도가 주요 관심사였던 이전 테스트에서 발견한

 JSON.parse(JSON.stringify(obj))

객체를 딥 클론하는 가장 느린 방법입니다( deep 플래그가 10-20%로 true로 설정된 jQuery.extend보다 느림).

deep 플래그가 false (얕은 복제)로 설정되면 jQuery.extend는 매우 빠릅니다. 유형 유효성 검사를 위한 몇 가지 추가 논리가 포함되어 있고 정의되지 않은 속성 등을 복사하지 않기 때문에 좋은 옵션이지만 속도가 약간 느려집니다.

복제하려는 객체의 구조를 알고 있거나 깊은 중첩 배열을 피할 수 있는 경우 for (var i in obj) 루프를 작성할 수 있으며 jQuery보다 훨씬 빠릅니다.

마지막으로 핫 루프에서 알려진 객체 구조를 복제하려는 경우 복제 절차를 인라인하고 객체를 수동으로 구성하여 훨씬 더 많은 성능을 얻을 수 있습니다.

JavaScript 추적 엔진은 for..in 루프를 최적화하는 데 실패하고 hasOwnProperty를 확인하면 속도가 느려집니다. 속도가 절대적으로 필요한 경우 수동 복제.

 var clonedObject = { knownProp: obj.knownProp, .. }

Date 객체 JSON.parse(JSON.stringify(obj)) 메서드 사용에 주의하십시오. JSON.stringify(new Date()) JSON.parse() 가 다시 변환 하지 않는 ISO 형식의 날짜 문자열 표현을 반환합니다. Date 객체에. 자세한 내용은 이 답변을 참조하세요 .

또한 Chrome 65 이상에서는 기본 복제가 불가능합니다. JSPerf에 따르면 새 함수를 생성하여 기본 복제를 수행하는 것은 전반적으로 엄청나게 빠른 JSON.stringify를 사용하는 것보다 거의 800배 느립니다.

ES6용 업데이트

Javascript ES6을 사용하는 경우 복제 또는 얕은 복사를 위해 이 기본 방법을 시도하십시오.

 Object.assign({}, obj);


답변자 : Community Wiki


객체에 변수만 있고 함수가 없다고 가정하면 다음을 사용할 수 있습니다.

 var newObject = JSON.parse(JSON.stringify(oldObject));


답변자 : Jeremy


구조화된 복제

2021 업데이트: structuredClone 전역 기능 이 곧 브라우저, Node.js 및 Deno에 제공됩니다.

HTML 표준에는 객체의 깊은 복제를 생성할 수 있는 내부 구조화된 복제/직렬화 알고리즘이 포함되어 있습니다. 여전히 특정 내장 유형으로 제한되어 있지만 JSON에서 지원하는 몇 가지 유형 외에도 Dates, RegExps, Maps, Sets, Blob, FileLists, ImageDatas, sparse Arrays, Typed Arrays 및 아마도 더 많은 미래를 지원합니다. . 또한 복제된 데이터 내의 참조를 보존하여 JSON에 대한 오류를 일으킬 수 있는 순환 및 재귀 구조를 지원할 수 있습니다.

Node.js 지원: 실험적

structuredClone 전역 함수 는 곧 Node.js에서 제공됩니다.

 const clone = structuredClone(original);

그때까지: v8 모듈은 현재(Node 11 기준) 구조화된 직렬화 API를 직접 노출 하지만 이 기능은 여전히 "실험적"으로 표시되며 향후 버전에서 변경되거나 제거될 수 있습니다. 호환되는 버전을 사용하는 경우 개체 복제는 다음과 같이 간단합니다.

 const v8 = require('v8'); const structuredClone = obj => { return v8.deserialize(v8.serialize(obj)); };

브라우저에서 직접 지원: 곧 제공

structuredClone 전역 기능 은 곧 모든 주요 브라우저에서 제공될 예정입니다(이전 에 GitHub의 whatwg/html#793에서 논의한 바 있음). 다음과 같이 보일 것입니다.

 const clone = structuredClone(original);

이것이 출시될 때까지 브라우저의 구조화된 복제 구현은 간접적으로만 노출됩니다.

비동기식 해결 방법: 사용할 수 있습니다.

기존 API를 사용하여 구조화된 클론을 생성하는 오버헤드가 낮은 방법은MessageChannels 의 포트 하나를 통해 데이터를 게시하는 것입니다. 다른 포트는 .data 의 구조화된 복제와 함께 message 이벤트를 내보냅니다. 불행히도 이러한 이벤트 수신은 반드시 비동기식이며 동기식 대안은 덜 실용적입니다.

 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; };

사용 예:

 'use strict'; const main = () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = structuredClone(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."); }; const structuredClone = obj => { const oldState = history.state; history.replaceState(obj, null); const clonedObj = history.state; history.replaceState(oldState, null); return clonedObj; }; main();

동기식이지만 매우 느릴 수 있습니다. 브라우저 기록 조작과 관련된 모든 오버헤드가 발생합니다. 이 메서드를 반복적으로 호출하면 Chrome이 일시적으로 응답하지 않을 수 있습니다.

Notification 생성자 는 연결된 데이터의 구조화된 복제본을 만듭니다. 또한 사용자에게 브라우저 알림을 표시하려고 시도하지만 알림 권한을 요청하지 않는 한 자동으로 실패합니다. 다른 목적에 대한 권한이 있는 경우 생성한 알림을 즉시 닫습니다.

 const structuredClone = obj => { const n = new Notification('', {data: obj, silent: true}); n.onshow = n.close.bind(n); return n.data; };

사용 예:

 'use strict'; const main = () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = structuredClone(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."); }; const structuredClone = obj => { const n = new Notification('', {data: obj, silent: true}); n.close(); return n.data; }; main();



답변자 : Community Wiki


내장된 것이 없다면 다음을 시도할 수 있습니다.

 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; }


답변자 : Community Wiki


한 줄의 코드로 객체를 복제(딥 복제가 아님)하는 효율적인 방법

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; } }); }


답변자 : Community Wiki


이것은 내가 사용하는 것입니다 :

 function cloneObject(obj) { var clone = {}; for(var i in obj) { if(typeof(obj[i])=="object" && obj[i] != null) clone[i] = cloneObject(obj[i]); else clone[i] = obj[i]; } return clone; }


답변자 : Community Wiki


암호:

 // extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned function extend(from, to) { if (from == null || typeof from != "object") return from; if (from.constructor != Object && from.constructor != Array) return from; if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function || from.constructor == String || from.constructor == Number || from.constructor == Boolean) return new from.constructor(from); to = to || new from.constructor(); for (var name in from) { to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name]; } return to; }

시험:

 var obj = { date: new Date(), func: function(q) { return 1 + q; }, num: 123, text: "asdasd", array: [1, "asd"], regex: new RegExp(/aaa/i), subobj: { num: 234, text: "asdsaD" } } var clone = extend(obj);


답변자 : Community Wiki


JavaScript의 깊은 복사 객체 (가장 간단하고 가장 간단하다고 생각합니다)

1. JSON.parse(JSON.stringify(object)) 사용

 var obj = { a: 1, b: { c: 2 } } var newObj = JSON.parse(JSON.stringify(obj)); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } }

2. 생성된 메소드 사용

 function cloneObject(obj) { var clone = {}; for(var i in obj) { if(obj[i] != null && typeof(obj[i])=="object") clone[i] = cloneObject(obj[i]); else clone[i] = obj[i]; } return clone; } var obj = { a: 1, b: { c: 2 } } var newObj = cloneObject(obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } }

3. Lo-Dash의 _.cloneDeep 링크 lodash 사용

 var obj = { a: 1, b: { c: 2 } } var newObj = _.cloneDeep(obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } }

4. Object.assign() 메서드 사용

 var obj = { a: 1, b: 2 } var newObj = _.clone(obj); obj.b = 20; console.log(obj); // { a: 1, b: 20 } console.log(newObj); // { a: 1, b: 2 }

하지만 잘못된 경우

 var obj = { a: 1, b: { c: 2 } } var newObj = Object.assign({}, obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG // Note: Properties on the prototype chain and non-enumerable properties cannot be copied.

5. Underscore.js _.clone 링크 Underscore.js 사용

 var obj = { a: 1, b: 2 } var newObj = _.clone(obj); obj.b = 20; console.log(obj); // { a: 1, b: 20 } console.log(newObj); // { a: 1, b: 2 }

하지만 잘못된 경우

 var obj = { a: 1, b: { c: 2 } } var newObj = _.cloneDeep(obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG // (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)

JSBEN.CH 성능 벤치마킹 놀이터 1~3 http://jsben.ch/KVQLd JavaScript의 성능 심층 복사 개체



답변자 : Community Wiki


성능별 심층 카피: 최고에서 최악 순으로 순위 지정

  • 확산 연산자 ... (기본 배열 - 전용)
  • splice(0) (기본 배열 - 전용)
  • slice() (기본 배열 - 전용)
  • concat() (기본 배열 - 전용)
  • 아래와 같이 사용자 정의 함수(모든 배열)
  • jQuery의 $.extend() (모든 배열)
  • JSON.parse(JSON.stringify()) (기본 및 리터럴 배열 - 전용)
  • 밑줄의 _.clone() (기본 및 리터럴 배열 - 전용)
  • Lodash의 _.cloneDeep() (모든 배열)

어디에:

  • 프리미티브 = 문자열, 숫자 및 부울
  • 리터럴 = 객체 리터럴 {} , 배열 리터럴 []
  • 모든 = 프리미티브, 리터럴 및 프로토타입

기본 배열의 전체 복사:

 let arr1a = [1, 'a', true];

프리미티브(예: 숫자, 문자열 및 부울)만 있는 배열을 딥 복사하려면 재할당, slice() , concat() 및 Underscore의 clone() 사용할 수 있습니다.

스프레드의 성능이 가장 빠른 경우:

 let arr1b = [...arr1a];

그리고 slice() concat() 보다 성능이 더 좋은 곳 : https://jsbench.me/x5ktn7o94d/

 let arr1c = arr1a.splice(0); let arr1d = arr1a.slice(); let arr1e = arr1a.concat();

기본 및 객체 리터럴의 배열을 딥 복사합니다.

 let arr2a = [1, 'a', true, {}, []]; let arr2b = JSON.parse(JSON.stringify(arr2a));

프리미티브, 객체 리터럴 및 프로토타입의 배열을 딥 복사합니다.

 let arr3a = [1, 'a', true, {}, [], new Object()];

사용자 정의 함수를 작성하십시오( $.extend() 또는 JSON.parse 보다 성능이 빠름).

 function copy(o) { let out, v, key; out = Array.isArray(o) ? [] : {}; for (key in o) { v = o[key]; out[key] = (typeof v === "object" && v !== null) ? copy(v) : v; } return out; } let arr3b = copy(arr3a);

또는 타사 유틸리티 기능을 사용합니다.

 let arr3c = $.extend(true, [], arr3a); // jQuery Extend let arr3d = _.cloneDeep(arr3a); // Lodash

참고: jQuery의 $.extend JSON.parse(JSON.stringify()) 보다 성능도 좋습니다.



답변자 : Community Wiki


객체 복제는 JS에서 항상 문제였지만 ES6 이전에는 모두 문제였습니다. 아래 JavaScript에서 객체를 복사하는 다양한 방법을 나열합니다. 아래에 객체가 있고 이에 대한 깊은 사본을 갖고 싶어한다고 상상해 보십시오.

 var obj = {a:1, b:2, c:3, d:4};

원본을 변경하지 않고 이 개체를 복사하는 방법은 몇 가지가 있습니다.

  1. ES5+, 간단한 기능을 사용하여 복사:

     function deepCopyObj(obj) { if (null == obj || "object" != typeof obj) return obj; if (obj instanceof Date) { var copy = new Date(); copy.setTime(obj.getTime()); return copy; } if (obj instanceof Array) { var copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = deepCopyObj(obj[i]); } return copy; } if (obj instanceof Object) { var copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = deepCopyObj(obj[attr]); } return copy; } throw new Error("Unable to copy obj this object."); }
  2. ES5+, JSON.parseJSON.stringify .

     var deepCopyObj = JSON.parse(JSON.stringify(obj));
  3. 모난:

     var deepCopyObj = angular.copy(obj);
  4. 제이쿼리:

     var deepCopyObj = jQuery.extend(true, {}, obj);
  5. Underscore.js 및 Lodash:

     var deepCopyObj = _.cloneDeep(obj); //latest version of Underscore.js makes shallow copy

이러한 도움을 바랍니다…



답변자 : Community Wiki


var clone = function() { var newObj = (this instanceof Array) ? [] : {}; for (var i in this) { if (this[i] && typeof this[i] == "object") { newObj[i] = this[i].clone(); } else { newObj[i] = this[i]; } } return newObj; }; Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});


답변자 : Community Wiki


이 작업을 아주 잘 수행하는 라이브러리("복제"라고 함)가 있습니다. 그것은 내가 아는 임의의 객체에 대한 가장 완전한 재귀적 복제/복사를 제공합니다. 또한 아직 다른 답변에서 다루지 않는 순환 참조를 지원합니다.

npm 에서도 찾을 수 있습니다. 브라우저는 물론 Node.js에서도 사용할 수 있습니다.

사용 방법에 대한 예는 다음과 같습니다.

로 설치

 npm install clone

또는 Ender 로 패키징하십시오.

 ender build clone [...]

소스 코드를 수동으로 다운로드할 수도 있습니다.

그런 다음 소스 코드에서 사용할 수 있습니다.

 var clone = require('clone'); var a = { foo: { bar: 'baz' } }; // inital value of a var b = clone(a); // clone a -> b a.foo.bar = 'foo'; // change a console.log(a); // { foo: { bar: 'foo' } } console.log(b); // { foo: { bar: 'baz' } }

(면책 조항: 저는 라이브러리의 저자입니다.)



답변자 : Community Wiki


나는 이것이 오래된 게시물이라는 것을 알고 있지만 이것이 우연히 따라 다니는 다음 사람에게 약간의 도움이 될 수 있다고 생각했습니다.

객체를 아무 것에 할당하지 않는 한 메모리에 참조를 유지하지 않습니다. 따라서 다른 객체와 공유하려는 객체를 만들려면 다음과 같이 팩토리를 생성해야 합니다.

 var a = function(){ return { father:'zacharias' }; }, b = a(), c = a(); c.father = 'johndoe'; alert(b.father);


답변자 : Community Wiki


사용하는 경우 Underscore.js 라이브러리에 복제 메서드가 있습니다.

 var newObject = _.clone(oldObject);


답변자 : Community Wiki


다음은 생성자에 필수 매개변수가 있는 경우에도 작동하는 위의 ConroyP 답변 버전입니다.

 //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that's all we need here var object_create = Object.create; if (typeof object_create !== 'function') { object_create = function(o) { function F() {} F.prototype = o; return new F(); }; } function deepCopy(obj) { if(obj == null || typeof(obj) !== 'object'){ return obj; } //make sure the returned object has the same prototype as the original var ret = object_create(obj.constructor.prototype); for(var key in obj){ ret[key] = deepCopy(obj[key]); } return ret; }

이 기능은 내 simpleoo 라이브러리에서도 사용할 수 있습니다.

편집하다:

다음은 보다 강력한 버전입니다(Justin McCandless 덕분에 이제 순환 참조도 지원합니다).

 /** * Deep copy an object (make copies of all its object properties, sub-properties, etc.) * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone * that doesn't break if the constructor has required parameters * * It also borrows some code from http://stackoverflow.com/a/11621004/560114 */ function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) { if(src === null || typeof(src) !== 'object'){ return src; } //Honor native/custom clone methods if(typeof src.clone == 'function'){ return src.clone(true); } //Special cases: //Date if(src instanceof Date){ return new Date(src.getTime()); } //RegExp if(src instanceof RegExp){ return new RegExp(src); } //DOM Element if(src.nodeType && typeof src.cloneNode == 'function'){ return src.cloneNode(true); } // Initialize the visited objects arrays if needed. // This is used to detect cyclic references. if (_visited === undefined){ _visited = []; _copiesVisited = []; } // Check if this object has already been visited var i, len = _visited.length; for (i = 0; i < len; i++) { // If so, get the copy we already made if (src === _visited[i]) { return _copiesVisited[i]; } } //Array if (Object.prototype.toString.call(src) == '[object Array]') { //[].slice() by itself would soft clone var ret = src.slice(); //add it to the visited array _visited.push(src); _copiesVisited.push(ret); var i = ret.length; while (i--) { ret[i] = deepCopy(ret[i], _visited, _copiesVisited); } return ret; } //If we've reached here, we have a regular object //make sure the returned object has the same prototype as the original var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__); if (!proto) { proto = src.constructor.prototype; //this line would probably only be reached by very old browsers } var dest = object_create(proto); //add this object to the visited array _visited.push(src); _copiesVisited.push(dest); for (var key in src) { //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc. //For an example of how this could be modified to do so, see the singleMixin() function dest[key] = deepCopy(src[key], _visited, _copiesVisited); } return dest; } //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that's all we need here var object_create = Object.create; if (typeof object_create !== 'function') { object_create = function(o) { function F() {} F.prototype = o; return new F(); }; }


답변자 : Community Wiki


다음은 동일한 개체의 두 인스턴스를 만듭니다. 찾아서 현재 사용하고 있습니다. 간단하고 사용하기 쉽습니다.

 var objToCreate = JSON.parse(JSON.stringify(cloneThis));


답변자 : Community Wiki


Crockford는 이 함수를 사용하여 제안합니다.

 function object(o) { function F() {} F.prototype = o; return new F(); } var newObject = object(oldObject);

간결하고 예상대로 작동하며 라이브러리가 필요하지 않습니다.


편집하다:

Object.create 대한 폴리필이므로 이것을 사용할 수도 있습니다.

 var newObject = Object.create(oldObject);

참고: hasOwnProperty 를 사용하는 일부 반복에 문제가 있을 수 있습니다. 왜냐하면, create oldObject 를 상속하는 새로운 빈 객체를 생성하기 때문입니다. 그러나 객체 복제에는 여전히 유용하고 실용적입니다.

예를 들어 oldObject.a = 5;

 newObject.a; // is 5

하지만:

 oldObject.hasOwnProperty(a); // is true newObject.hasOwnProperty(a); // is false


답변자 : Community Wiki


Lodash에는 멋진 _.cloneDeep(value) 메서드가 있습니다.

 var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false


답변자 : Community Wiki


function clone(obj) { var clone = {}; clone.prototype = obj.prototype; for (property in obj) clone[property] = obj[property]; return clone; }


답변자 : Community Wiki


얕은 사본 한 줄 ( ECMAScript 5th edition ):

 var origin = { foo : {} }; var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{}); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true

얕은 카피 한 줄( ECMAScript 6th edition , 2015):

 var origin = { foo : {} }; var copy = Object.assign({}, origin); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true


답변자 : Community Wiki


배열과 같은 객체에 이상적인 딥 클론 연산자는 아직 없는 것 같습니다. 아래 코드에서 알 수 있듯이 John Resig의 jQuery 복제기는 숫자가 아닌 속성이 있는 배열을 배열이 아닌 객체로 변환하고 RegDwight의 JSON 복제기는 숫자가 아닌 속성을 삭제합니다. 다음 테스트는 여러 브라우저에서 이러한 점을 보여줍니다.

 function jQueryClone(obj) { return jQuery.extend(true, {}, obj) } function JSONClone(obj) { return JSON.parse(JSON.stringify(obj)) } var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]]; arrayLikeObj.names = ["m", "n", "o"]; var JSONCopy = JSONClone(arrayLikeObj); var jQueryCopy = jQueryClone(arrayLikeObj); alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) + "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) + "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names + "\nAnd what are the JSONClone names? " + JSONCopy.names)


답변자 : Community Wiki


AngularJS가 언급되지 않았고 사람들이 알고 싶어할 것이라고 생각했기 때문에...

angular.copy 는 객체와 배열을 딥 복사하는 방법도 제공합니다.



답변자 : Community Wiki


귀하의 목표가 "일반 오래된 JavaScript 객체"를 복제하는지 여부에 따라 두 가지 좋은 답변이 있습니다.

또한 원본 개체에 대한 프로토타입 참조가 없는 완전한 복제본을 만드는 것이 의도라고 가정해 보겠습니다. 완전한 복제에 관심이 없다면 다른 답변(Crockford의 패턴)에서 제공되는 많은 Object.clone() 루틴을 사용할 수 있습니다.

평범한 오래된 JavaScript 객체의 경우 현대 런타임에서 객체를 복제하는 시도되고 진정한 좋은 방법은 아주 간단합니다.

 var clone = JSON.parse(JSON.stringify(obj));

소스 객체는 순수 JSON 객체여야 합니다. 즉, 모든 중첩 속성은 스칼라여야 합니다(예: 부울, 문자열, 배열, 개체 등). RegExp 또는 Date와 같은 기능이나 특수 개체는 복제되지 않습니다.

효율적인가? 젠장 예. 우리는 모든 종류의 복제 방법을 시도했으며 이것이 가장 잘 작동합니다. 어떤 닌자가 더 빠른 방법을 생각해 낼 수 있다고 확신합니다. 그러나 나는 우리가 한계 이익에 대해 이야기하고 있다고 생각합니다.

이 접근 방식은 간단하고 구현하기 쉽습니다. 그것을 편의 기능으로 포장하고 실제로 이득을 짜내야 하는 경우 나중에 가십시오.

이제 일반 JavaScript 객체가 아닌 경우에는 정말 간단한 답이 없습니다. 사실 자바스크립트 함수와 내부 객체 상태의 동적 특성 때문에 그럴 수 없습니다. 내부에 함수가 있는 JSON 구조를 딥 복제하려면 해당 함수와 내부 컨텍스트를 다시 만들어야 합니다. 그리고 JavaScript에는 표준화된 방법이 없습니다.

이를 수행하는 올바른 방법은 코드 내에서 선언하고 재사용하는 편리한 메서드를 사용하는 것입니다. 편의 방법은 자신의 개체에 대한 약간의 이해를 부여할 수 있으므로 새 개체 내에서 그래프를 적절하게 다시 만들 수 있습니다.

우리는 우리 자신의 것을 작성했지만 내가 본 가장 일반적인 접근 방식은 여기에서 다룹니다.

http://davidwalsh.name/javascript-clone

이것은 올바른 생각입니다. 저자(David Walsh)는 일반화된 기능의 복제에 대해 언급했습니다. 이것은 사용 사례에 따라 선택할 수 있는 작업입니다.

주요 아이디어는 유형별로 함수(또는 프로토타입 클래스)의 인스턴스화를 특별히 처리해야 한다는 것입니다. 여기에서 그는 RegExp 및 Date에 대한 몇 가지 예를 제공했습니다.

이 코드는 간단할 뿐만 아니라 매우 읽기 쉽습니다. 확장하는 것은 매우 쉽습니다.

이것이 효율적인가? 젠장 예. 목표가 진정한 딥 카피 클론을 생성하는 것이라는 점을 감안할 때 소스 개체 그래프의 구성원을 살펴봐야 합니다. 이 접근 방식을 사용하면 처리할 하위 멤버와 사용자 지정 유형을 수동으로 처리하는 방법을 정확하게 조정할 수 있습니다.

그래서 당신은 간다. 두 가지 접근 방식. 제 생각에는 둘 다 효율적입니다.



답변자 : Community Wiki


이 질문에 답하기에는 늦었지만 개체를 복제하는 다른 방법이 있습니다.

 function cloneObject(obj) { if (obj === null || typeof(obj) !== 'object') return obj; var temp = obj.constructor(); // changed for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { obj['isActiveClone'] = null; temp[key] = cloneObject(obj[key]); delete obj['isActiveClone']; } } return temp; } var b = cloneObject({"a":1,"b":2}); // calling

훨씬 더 좋고 빠릅니다.

 var a = {"a":1,"b":2}; var b = JSON.parse(JSON.stringify(a));

그리고

 var a = {"a":1,"b":2}; // Deep copy var newObject = jQuery.extend(true, {}, a);

코드를 벤치마킹했으며 여기 에서 결과를 테스트할 수 있습니다.

결과 공유: 여기에 이미지 설명 입력 참조: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty



답변자 : Community Wiki


이것은 일반적으로 가장 효율적인 솔루션은 아니지만 필요한 작업을 수행합니다. 아래의 간단한 테스트 사례...

 function clone(obj, clones) { // Makes a deep copy of 'obj'. Handles cyclic structures by // tracking cloned obj's in the 'clones' parameter. Functions // are included, but not cloned. Functions members are cloned. var new_obj, already_cloned, t = typeof obj, i = 0, l, pair; clones = clones || []; if (obj === null) { return obj; } if (t === "object" || t === "function") { // check to see if we've already cloned obj for (i = 0, l = clones.length; i < l; i++) { pair = clones[i]; if (pair[0] === obj) { already_cloned = pair[1]; break; } } if (already_cloned) { return already_cloned; } else { if (t === "object") { // create new object new_obj = new obj.constructor(); } else { // Just use functions as is new_obj = obj; } clones.push([obj, new_obj]); // keep track of objects we've cloned for (key in obj) { // clone object members if (obj.hasOwnProperty(key)) { new_obj[key] = clone(obj[key], clones); } } } } return new_obj || obj; }

순환 배열 테스트...

 a = [] a.push("b", "c", a) aa = clone(a) aa === a //=> false aa[2] === a //=> false aa[2] === a[2] //=> false aa[2] === aa //=> true

기능 검사...

 f = new Function fa = a ff = clone(f) ff === f //=> true ff.a === a //=> false


답변자 : Community Wiki


JSON.parse(JSON.stringify(obj)) 버전을 사용하고 싶지만 Date 객체를 잃지 않으려면 parse 메소드의 두 번째 인수를 사용하여 문자열을 다시 Date로 변환할 수 있습니다.

 function clone(obj) { var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; return JSON.parse(JSON.stringify(obj), function(k, v) { if (typeof v === 'string' && regExp.test(v)) return new Date(v) return v; }) } // usage: var original = { a: [1, null, undefined, 0, {a:null}, new Date()], b: { c(){ return 0 } } } var cloned = clone(original) console.log(cloned)



답변자 : Community Wiki


나는 여기 에서 가장 많은 표를 얻은 답변에 동의하지 않습니다. 재귀 딥 클론 은 언급된 JSON.parse(JSON.stringify(obj)) 접근 방식보다 훨씬 빠릅니다.

빠른 참조를 위한 기능은 다음과 같습니다.

 function cloneDeep (o) { let newO let i if (typeof o !== 'object') return o if (!o) return o if (Object.prototype.toString.apply(o) === '[object Array]') { newO = [] for (i = 0; i < o.length; i += 1) { newO[i] = cloneDeep(o[i]) } return newO } newO = {} for (i in o) { if (o.hasOwnProperty(i)) { newO[i] = cloneDeep(o[i]) } } return newO }


답변자 : Community Wiki


ECMAScript 6 또는 트랜스파일러를 사용할 수 있는 경우에만.

특징:

  • 복사하는 동안 getter/setter를 트리거하지 않습니다.
  • getter/setter를 보존합니다.
  • 프로토타입 정보를 유지합니다.
  • 객체 리터럴기능적 OO 쓰기 스타일 모두에서 작동합니다.

암호:

 function clone(target, source){ for(let key in source){ // Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter. let descriptor = Object.getOwnPropertyDescriptor(source, key); if(descriptor.value instanceof String){ target[key] = new String(descriptor.value); } else if(descriptor.value instanceof Array){ target[key] = clone([], descriptor.value); } else if(descriptor.value instanceof Object){ let prototype = Reflect.getPrototypeOf(descriptor.value); let cloneObject = clone({}, descriptor.value); Reflect.setPrototypeOf(cloneObject, prototype); target[key] = cloneObject; } else { Object.defineProperty(target, key, descriptor); } } let prototype = Reflect.getPrototypeOf(source); Reflect.setPrototypeOf(target, prototype); return target; }


답변자 : Community Wiki


다음은 모든 JavaScript 객체를 복제할 수 있는 포괄적인 clone() 메서드입니다. 거의 모든 경우를 처리합니다.

 function clone(src, deep) { var toString = Object.prototype.toString; if (!src && typeof src != "object") { // Any non-object (Boolean, String, Number), null, undefined, NaN return src; } // Honor native/custom clone methods if (src.clone && toString.call(src.clone) == "[object Function]") { return src.clone(deep); } // DOM elements if (src.nodeType && toString.call(src.cloneNode) == "[object Function]") { return src.cloneNode(deep); } // Date if (toString.call(src) == "[object Date]") { return new Date(src.getTime()); } // RegExp if (toString.call(src) == "[object RegExp]") { return new RegExp(src); } // Function if (toString.call(src) == "[object Function]") { //Wrap in another method to make sure == is not true; //Note: Huge performance issue due to closures, comment this :) return (function(){ src.apply(this, arguments); }); } var ret, index; //Array if (toString.call(src) == "[object Array]") { //[].slice(0) would soft clone ret = src.slice(); if (deep) { index = ret.length; while (index--) { ret[index] = clone(ret[index], true); } } } //Object else { ret = src.constructor ? new src.constructor() : {}; for (var prop in src) { ret[prop] = deep ? clone(src[prop], true) : src[prop]; } } return ret; };


출처 : Here


출처 : http:www.stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript">

반응형