Який найбільш ефективний спосіб глибокого клонування об'єкта в 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, по крайней мере, нативний клонування не підходить. Згідно цей 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 в 1:47 2009-12-12 1: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-Dash _.cloneDeep link 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 link 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