Які найбольш эфектыўны спосаб глыбокага кланавання аб'екта ў JavaScript?

Які найбольш эфектыўны спосаб кланавання аб'екта JavaScript? Я бачыў obj = eval(uneval(o)); , Але які нестандарт і падтрымліваецца толькі Firefox .

Я 'вы рабілі такія рэчы, як obj = JSON.parse(JSON.stringify(o)); , Але сумняваецеся ў эфектыўнасці.

Я таксама бачыў рэкурсіўныя функцыі капіявання з рознымі недахопамі.

Я здзіўлены, што кананічнага рашэння не існуе.

4845
23 сент. зададзены jschrab 23 сент. 2008-09-23 19:26 '08 у 19:26 2008-09-23 19:26
@ 69 адказаў
  • 1
  • 2
  • 3

Заўвага: Гэта адказ на іншы адказ, а не правільны адказ на гэтае пытанне. Калі вы хочаце хутка кланаваць аб'екты, калі ласка, прытрымлівайцеся радзе Corban ў сваім адказе на гэтае пытанне.


Хачу адзначыць, што метад .clone() у jQuery толькі клануецца элементы DOM. Каб кланаваць аб'екты JavaScript, вы павінны:

 // Shallow copy var newObject = jQuery.extend({}, oldObject); // Deep copy var newObject = jQuery.extend(true, {}, oldObject); 

Больш падрабязную інфармацыю можна знайсці ў дакументацыі jQuery .

Я таксама хачу адзначыць, што глыбокая копія на самай справе нашмат разумней, чым паказана вышэй - яна здольная пазбегнуць многіх пастак (напрыклад, для глыбокага пашырэння элемента DOM). Ён часта выкарыстоўваецца ў ядры jQuery і ў убудовах з вялікім эфектам.

4203
23 сент. адказ дадзены John Resig 23 сент. 2008-09-23 21:09 '08 а 21:09 2008-09-23 21:09

Азнаёмцеся з гэтым эталонам: http://jsben.ch/#/bWfk9

У маіх папярэдніх тэстах, дзе хуткасць была галоўнай праблемай, я выявіў

<Папярэдне> <код> JSON.parse (JSON.stringify (OBJ)) Код>

каб быць самым хуткім спосабам глыбокага кланавання аб'екта (ён пераўзыходзіць jQuery.extend з усталяваным глыбокім сцягам на 10-20%).

jQuery.extend даволі хутка, калі для сцяга глыбокага значэння ўстаноўлена значэнне false (дробны клон). Гэта добры варыянт, паколькі ён уключае ў сябе дадатковую логіку для праверкі тыпаў і не капіруе-над уласцівасцяў undefined і г.д., Але гэта таксама трохі замарудзіць вас.

Калі вы ведаеце структуру аб'ектаў, якія вы спрабуеце кланаваць або можаце пазбегнуць глыбокіх ўкладзеных масіваў, вы можаце напісаць просты цыкл for (var я in obj) , каб кланаваць ваш аб'ект, правяраючы hasOwnProperty і ён будзе нашмат хутчэй, чым jQuery.

border=0

Нарэшце, калі вы спрабуеце кланаваць вядомую структуру аб'екта ў гарачым цыкле, вы можаце атрымаць MUCH MORE MORE PERFORMANCE, проста уставіўшы працэдуру кланавання і ўручную стварыўшы аб'ект.

Механізмы адсочвання JavaScript смактаць пры аптымізацыі цыклаў for..in і праверка hasOwnProperty таксама замарудзіць вас. Ручной клон, калі хуткасць з'яўляецца абсалютнай неабходнасцю.

  var clonedObject = { knownProp: obj.knownProp,.. } Код> 

Сцеражыцеся выкарыстання метаду JSON.parse(JSON.stringify(obj)) для аб'ектаў Date - JSON.stringify(новая дата()) вяртае радковае ўяўленне даты ў фармаце ISO, у якім JSON.parse() ня ня вяртаецца назад да аб'екта Date . Гл. Гэты адказ для больш падрабязнай інфармацыі .

Акрамя таго, звярніце ўвагу, што ў Chrome 65, па меншай меры, натыўны кланаванне не падыходзiць. Згодна гэты JSPerf , выкананне ўласнай кланавання шляхам стварэння новай функцыі амаль 800x павольней, чым выкарыстанне JSON.stringify, якое неверагодна хутка праходзіць праз усю дошку.

2046
17 марта '11 в 22:19 2011-03-17 22:19 адказ дадзены Corban Brook 17 сакавіка '11 у 22:19 2011-03-17 22:19

Мяркуючы, што ў вас ёсць толькі зменныя, а не якія-небудзь функцыі ў вашым аб'екце, вы можаце проста выкарыстоўваць:

 var newObject = JSON.parse(JSON.stringify(oldObject)); 
431
04 янв. адказ дадзены Sultan Shakir 04 студз. 2011-01-04 11:05 '11 у 11:05 2011-01-04 11:05

структураванае кланаванне

Стандарт HTML ўключае ўнутраны структураваны алгарытм кланавання / серыялізацыі, які можа ствараць глыбокія клоны аб'ектаў. Ён па-ранейшаму абмежаваны пэўнымі ўбудаванымі тыпамі, але ў дадатак да некалькіх тыпах, падтрымоўваным JSON, ён таксама падтрымлівае Dates, RegExps, Карты, Наборы, BLOB-аб'екты, FileLists, ImageDatas, разрэджаныя масівы, Typed Arrays і, магчыма, больш у будучыні ., Ён таксама захоўвае спасылкі ў кланаваных дадзеных, што дазваляе падтрымліваць цыклічныя і рэкурсіўныя структуры, якія могуць выклікаць памылкі для JSON.

Падтрымка ў Node.js: эксперыментальная 🙂

Модуль v8 ў Node.js ў цяперашні час (па стане на Node 11) наўпрост прадастаўляе API структураванай серыялізацыі , але гэтая функцыянальнасць па-ранейшаму пазначана як "эксперыментальная" і можа быць зменена або выдалена ў будучых версіях. Калі вы выкарыстоўваеце сумяшчальную версію, кланаванне аб'екта так жа проста, як:

structdClone structuredClone() абмяркоўвалася ў whatwg / html # 793 на GitHub .  У цяперашні час прапануецца выкарыстоўваць яго для большасці мэтаў так жа проста, як: 

MessageChannels .  Іншы порт адправіць падзея message са структураваным клонам прымацаваных дадзеных .data .  На жаль, праслухоўванне гэтых падзей абавязкова асінхронна, а сінхронныя альтэрнатывы менш практычныя. 

 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 . Вы можаце выкарыстоўваць гэта для стварэння структураванага клона любога аб'екта, падобнага на гэта:

Notification стварае структураваны клон звязаных з ім дадзеных.  Ён таксама спрабуе адлюстраваць апавяшчэнне ў браўзэры для карыстальніка, але гэта моўчкі завершыцца памылкай, калі вы не запыталі дазвол на апавяшчэнне.  Калі ў вас ёсць дазвол на іншыя мэты, мы неадкладна закрыем створанае намі апавяшчэнне. 

306
06 июня '12 в 17:59 2012-06-06 17:59 адказ дадзены Jeremy Banks 06 чэрвеня '12 у 17:59 2012-06-06 17:59

Калі б не было убудаванага, вы маглі б паспрабаваць:

 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; } 
294
23 сент. адказ дадзены ConroyP 23 сент. 2008-09-23 19:38 '08 у 19:38 2008-09-23 19:38

Эфектыўны спосаб кланавання (не глыбокага кланавання) аб'екта ў адным радку кода

Метад Object.assign з'яўляецца часткай стандарту ECMAScript 2015 (ES6) і робіць менавіта тое, што вам трэба.

 var clone = Object.assign({}, obj); 

Метад Object.assign () выкарыстоўваецца для капіявання значэнняў ўсіх перечислимых уласных уласцівасцяў з аднаго або некалькіх зыходных аб'ектаў у мэтавай аб'ект.

Больш падрабязна ...

polyfill для падтрымкі старых браўзэраў:

 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; } }); } 
148
15 дек. адказ дадзены Eugene Tiurin 15 снеж. 2015-12-15 10:26 '15 у 10:26 2015/12/15 10:26

код:

 // 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); 
94
25 июня '09 в 10:53 2009-06-25 10:53 адказ дадзены Kamarey 25 чэрвеня '09 у 10:53 2009-06-25 10:53

Гэта тое, што я выкарыстоўваю:

 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; } 
86
12 дек. адказ дадзены Alan 12 снеж. 2009-12-12 01:47 '09 у 01:47 2009-12-12 01:47

Глыбокая копія па прадукцыйнасці: ад лепшых да горшых

  • Перапрызначэння "=" (радковыя масівы, лікавыя масівы - толькі)
  • Slice (радковыя масівы, масівы лікаў - толькі)
  • Канкатэнацыя (толькі масівы радкоў, лікавыя масівы)
  • Карыстацкая функцыя: for-loop або рэкурсіўная копія
  • jQuery $ .extend
  • JSON.parse (толькі радковыя масівы, масівы лікаў, масівы аб'ектаў)
  • Underscore.js _.clone (толькі радковыя масівы, лікавыя масівы)
  • Lo-Dash _.cloneDeep

Глыбока скапіруйце масіў радкоў або лікаў (адзін узровень - без паказальнікаў):

Калі масіў змяшчае колькасці і радкі - такія функцыі, как.slice () ,. concat () ,. splice (), аператар прысвойвання "=" і функцыя clone Underscore.js; зробіць глыбокую копію элементаў масіва.

Калі перапрызначэння мае самую высокую прадукцыйнасць:

 var arr1 = ['a', 'b', 'c']; var arr2 = arr1; arr1 = ['a', 'b', 'c']; 

И.slice () мае лепшую прадукцыйнасць, чем.concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3

 var arr1 = ['a', 'b', 'c']; // Becomes arr1 = ['a', 'b', 'c'] var arr2a = arr1.slice(0); // Becomes arr2a = ['a', 'b', 'c'] - deep copy var arr2b = arr1.concat(); // Becomes arr2b = ['a', 'b', 'c'] - deep copy 

Глыбока скапіруйце масіў аб'ектаў (два ці больш узроўняў - паказальнікі):

 var arr1 = [{object:'a'}, {object:'b'}]; 

Напішыце карыстацкую функцыю (мае больш высокую прадукцыйнасць, чым $ .extend () або JSON.parse):

 function copy(o) { var 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; } copy(arr1); 

Выкарыстоўвайце іншыя службовыя функцыі:

 $.extend(true, [], arr1); // Jquery Extend JSON.parse(arr1); _.cloneDeep(arr1); // Lo-dash 

Дзе jQuery $ .extend мае лепшую прадукцыйнасць:

71
18 сент. адказ дадзены tfmontague 18 сент. 2014-09-18 23:10 '14 а 23:10 2014/09/18 23:10
 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}); 
60
26 дек. адказ дадзены Zibri 26 снеж. 2009-12-26 17:59 '09 у 17:59 2009-12-26 17:59

Я ведаю, што гэта стары пост, але я падумаў, што гэта можа дапамагчы каму-то, хто спатыкаецца.

Пакуль вы не прысвойваеце аб'ект чаму-небудзь, ён не падтрымлівае спасылку ў памяці. Такім чынам, каб стварыць аб'ект, які вы хочаце падзяліць паміж іншымі аб'ектамі, вам трэба стварыць factory так:

 var a = function(){ return { father:'zacharias' }; }, b = a(), c = a(); c.father = 'johndoe'; alert(b.father); 
53
24 сент. адказ дадзены Joe 24 сент. 2011-09-24 22:28 '11 а 22:28 2011-09-24 22:28

Cloning Аб'ект заўсёды быў прадметам турботы ў 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] = cloneSO(obj[i]); } return copy; } if (obj instanceof Object) { var copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]); } return copy; } throw new Error("Unable to copy obj this object."); } 

2) ES5 +, выкарыстоўваючы JSON.parse і JSON.stringify.

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

3) AngularJs:

 var deepCopyObj = angular.copy(obj); 

4) jQuery:

 var deepCopyObj = jQuery.extend(true, {}, obj); 

5) UnderscoreJs і Loadash:

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

Спадзяюся, што гэтая дапамога ...

52
03 апр. адказ дадзены Alireza 03 крас. 2017-04-03 18:37 '17 у 18:37 2017/04/03 18:37

Theres a бібліятэка (званая "клон") , што робіць гэта даволі добра. Ён забяспечвае найбольш поўнае рэкурсіўнае кланаванне / капіяванне адвольных аб'ектаў, пра якія я ведаю. Ён таксама падтрымлівае цыркулярныя спасылкі, якія яшчэ не ахопліваюцца іншымі адказамі.

Вы можаце знайсці яго на 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' } } 

(Адмова ад адказнасці: Im аўтар бібліятэкі.)

51
17 окт. адказ дадзены pvorb 17 каст. 2012-10-17 21:36 '12 а 21:36 2012/10/17 21:36

Калі вы выкарыстоўваеце яго, бібліятэка Underscore.js мае clone .

 var newObject = _.clone(oldObject); 
48
15 дек. адказ дадзены itsadok 15 снеж. 2011-12-15 18:56 '11 у 18:56 2011-12-15 18:56

Тут прыведзеная вышэй версія ConroyP, якая працуе, нават калі ў канструктара патрабуюцца параметры:

 //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that 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; } prototype as the original //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that 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 .

Edit:

Тут больш надзейная версія (дзякуючы Джасціну МакКэндлесу, цяпер гэта падтрымлівае цыклічныя спасылкі):

  function deepCopy(src,  _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 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(); }; } required parameters  function deepCopy(src,  _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 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(); }; } prototype as the original  function deepCopy(src,  _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 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(); }; } very old browsers  function deepCopy(src,  _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 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(); }; } modified to do so  function deepCopy(src,  _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 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(); }; } 
36
11 нояб. адказ дадзены Matt Browne 11 лістапада. 2012-11-11 20:53 '12 а 20:53 2012/11/11 20:53

Наступнае стварае два асобніка аднаго і таго ж аб'екта. Я знайшоў яго і выкарыстоўваю яго ў цяперашні час. Гэта просты і просты ў выкарыстанні.

 var objToCreate = JSON.parse(JSON.stringify(cloneThis)); 
31
21 авг. адказ дадзены nathan rogers 21 жнів. 2015-08-21 18:51 '15 у 18:51 2015/08/21 18:51

Глыбокае капіраванне аб'ектаў у 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-_.cloneDeep Link Dash 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. Выкарыстанне _.clone Link Underscore.js 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.) 

спасылка medium.com

JSBEN.CH Прадукцыйнасць Benchmarking Playground 1 ~ 3 http://jsben.ch/KVQLd 2019

08 авг. адказ дадзены TinhNQ 08 жнів. 2018-08-08 11:17 '18 у 11:17 2018/08/08 11:17

Lodash мае добры метад _. cloneDeep (значэнне) :

 var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false 
23
22 июня '13 в 18:03 2013-06-22 18:03 адказ дадзены opensas 22 чэрвеня '13 у 18:03 2013/06/22 18:03

Крокфорд прапаноўвае (і я аддаю перавагу) выкарыстоўваць гэтую функцыю:

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

Гэта кароткае, працуе так, як чакалася, і вам не патрэбна бібліятэка.


EDIT:

Гэта polyfill для 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 
23
06 окт. адказ дадзены Chris Broski 06 каст. 2010-10-06 18:08 '10 у 18:08 2010-10-06 18:08
 function clone(obj) { var clone = {}; clone.prototype = obj.prototype; for (property in obj) clone[property] = obj[property]; return clone; } 
22
23 сент. адказ дадзены Mark Cidade 23 сент. 2008-09-23 19:45 '08 у 19:45 2008-09-23 19:45

Однострочный копія дробнай копіі ( ECMAScript 5-е выданне ):

 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 
20
05 июля '12 в 0:44 2012-07-05 00:44 адказ дадзены Maël Nison 05 ліпеня '12 ў 0:44 2012-07-05 00:44

Просто потому, что я не видел AngularJS и думал, что люди захотят узнать...

angular.copy также предоставляет метод глубокого копирования объектов и массивов.

17
ответ дан Dan Atkinson 14 мая '16 в 1:16 2016-05-14 01:16