Каков наиболее эффективный способ глубокого клонирования объекта в JavaScript?

Каков наиболее эффективный способ клонирования объекта JavaScript? Я видел, obj = eval(uneval(o));как используется, но это нестандартно и поддерживается только Firefox .

Я сделал что-то вроде, obj = JSON.parse(JSON.stringify(o));но поставил под сомнение эффективность.

Я также видел рекурсивные функции копирования с различными недостатками.
Я удивлен, что канонического решения не существует.

23.09.2008 16:26:09
Эвал не злой. Использование Eval плохо это. Если вы боитесь его побочных эффектов, вы используете его неправильно. Побочные эффекты, которых вы боитесь, являются причинами его использования. Кто-нибудь, кстати, на самом деле ответил на ваш вопрос?
MageProspero 22.03.2012 14:08:54
Клонирование объектов - сложная задача, особенно с пользовательскими объектами произвольных коллекций. Который, вероятно, почему нет готового способа сделать это.
b01 11.03.2013 22:25:34
eval()это вообще плохая идея, потому что многие оптимизаторы движка Javascript вынуждены отключаться при работе с переменными, которые устанавливаются черезeval . Просто наличие eval()в вашем коде может привести к снижению производительности.
user56reinstatemonica8 8.09.2014 13:37:45
John Slegers 21.02.2016 18:21:08
Обратите внимание, что JSONметод потеряет все типы Javascript, которые не имеют эквивалента в JSON. Например: JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))сгенерирует{a: null, b: null, c: null, g: false}
oriadam 24.05.2017 13:06:47
30 ОТВЕТОВ
РЕШЕНИЕ

Родное глубокое клонирование

Он называется «структурированное клонирование», работает экспериментально в Node 11 и более поздних версиях и, надеюсь, появится в браузерах. Смотрите этот ответ для более подробной информации.

Быстрое клонирование с потерей данных - JSON.parse / stringify

Если вы не используете Dateс, функцией, undefined, Infinity, Regexps, карты, наборы, Blobs, списки файлов, ImageDatas разреженных массивов, типизированные массивы или другими сложными типами в пределах вашего объекта, очень простой один вкладыш к глубоким клонировать объект:

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 для ориентиров.

Надежное клонирование с использованием библиотеки

Поскольку клонирование объектов не является тривиальным (сложные типы, циклические ссылки, функции и т. Д.), Большинство основных библиотек предоставляют функции для клонирования объектов. Не изобретайте колесо - если вы уже используете библиотеку, проверьте, есть ли у нее функция клонирования объектов. Например,

  • лодаш - cloneDeep; может быть импортирован отдельно через модуль lodash.clonedeep и, вероятно, является вашим лучшим выбором, если вы еще не используете библиотеку, которая обеспечивает функцию глубокого клонирования
  • AngularJS - angular.copy
  • jQuery - jQuery.extend(true, { }, oldObject); .clone()только клоны DOM-элементов

ES6

Для полноты обратите внимание, что ES6 предлагает два механизма поверхностного копирования: Object.assign()и синтаксис распространения . который копирует значения всех перечисляемых собственных свойств из одного объекта в другой. Например:

var A1 = {a: "2"};
var A2 = Object.assign({}, A1);
var A3 = {...A1};  // Spread Syntax
4672
28.01.2020 15:32:11
@ThiefMaster github.com/jquery/jquery/blob/master/src/core.js в строке 276 (есть немного кода, который делает что-то еще, но код "как это сделать в JS" есть :)
Rune FS 27.03.2013 08:16:19
Вот код JS, стоящий за глубокой копией jQuery, для всех, кто интересуется: github.com/jquery/jquery/blob/master/src/core.js#L265-327
Alex W 11.04.2013 14:24:13
Ого! Просто чтобы быть предельно ясным: не знаю, почему этот ответ был выбран как правильный ответ, это был ответ на ответы, приведенные ниже: stackoverflow.com/a/122190/6524 (который рекомендовал .clone(), а это не тот код, который нужно используя в этом контексте). К сожалению, этот вопрос подвергся стольким пересмотрам, что первоначальная дискуссия уже не очевидна! Пожалуйста, просто следуйте советам Корбана и напишите цикл или скопируйте свойства непосредственно в новый объект, если вам нужна скорость. Или проверьте это сами!
John Resig 21.01.2014 03:37:38
Это вопрос JavaScript (без упоминания о jQuery).
gphilip 22.01.2015 08:30:39
Как можно было бы сделать это без использования JQuery?
Awesomeness01 30.04.2015 02:22:57

Проверьте этот тест: http://jsben.ch/#/bWfk9

В моих предыдущих тестах, где скорость была главной проблемой, я обнаружил

JSON.parse(JSON.stringify(obj))

быть самым медленным способом глубокого клонирования объекта (это медленнее, чем jQuery.extend с deepустановленным флагом true на 10-20%).

jQuery.extend довольно быстро работает, когда установлен deepфлаг false(мелкий клон). Это хороший вариант, потому что он включает некоторую дополнительную логику для проверки типа и не копирует неопределенные свойства и т. Д., Но это также немного замедлит работу.

Если вы знаете структуру объектов, которые вы пытаетесь клонировать, или можете избежать вложенных массивов, вы можете написать простой for (var i in obj)цикл для клонирования вашего объекта при проверке hasOwnProperty, и он будет намного быстрее, чем jQuery.

Наконец, если вы пытаетесь клонировать известную структуру объекта в горячем цикле, вы можете получить НАМНОГО БОЛЬШЕ ЭФФЕКТИВНОСТИ, просто вставив процедуру клонирования и вручную создав объект.

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

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

Остерегайтесь использования JSON.parse(JSON.stringify(obj))метода для Dateобъектов - JSON.stringify(new Date())возвращает строковое представление даты в формате ISO, которое JSON.parse() не преобразуется обратно в Dateобъект. Смотрите этот ответ для более подробной информации .

Кроме того, обратите внимание, что, по крайней мере, в Chrome 65, клонирование по-родному не подходит. По словам JSPerf, выполнение нативного клонирования путем создания новой функции почти в 800 раз медленнее, чем при использовании JSON.stringify, который невероятно быстр по всем направлениям.

Обновление для ES6

Если вы используете Javascript ES6, попробуйте этот собственный метод для клонирования или поверхностного копирования.

Object.assign({}, obj);
2256
20.11.2019 04:31:32
@trysis Object.create не клонирует объект, использует объект-прототип ... jsfiddle.net/rahpuser/yufzc1jt/2
rahpuser 25.11.2014 21:37:12
Этот метод также удалит keysиз вашего object, у которого есть functionsих значения, потому что JSONон не поддерживает функции.
Karlen Kishmiryan 29.05.2015 09:52:16
Также имейте в виду, что использование JSON.parse(JSON.stringify(obj))объектов Date также преобразует дату обратно в UTC в строковом представлении в формате ISO8601 .
dnlgmzddr 30.07.2015 21:37:10
Подход JSON также подавляет циклические ссылки.
rich remer 13.02.2016 05:25:15
@velop, Object.assign ({}, objToClone), похоже, делает мелкий клон - используя его во время игры в консоли инструментов разработчика, клон объекта все еще указывает на ссылку на клонированный объект. Так что я не думаю, что это действительно применимо здесь.
Garrett Simpson 3.11.2016 18:00:14

Предполагая, что в вашем объекте есть только переменные, а не какие-либо функции, вы можете просто использовать:

var newObject = JSON.parse(JSON.stringify(oldObject));
472
23.10.2014 23:47:23
зэк такого подхода , как я только что нашел, если ваш объект имеет какие - либо функции (шахта имеет внутренние добытчик & сеттер) , то они теряются , когда строковые .. Если это все , что вам нужен этот метод будет хорошо ..
Markive 17.01.2013 11:00:08
@Jason, причина, по которой этот метод медленнее, чем поверхностное копирование (для глубокого объекта), заключается в том, что этот метод по определению является глубоким копированием. Но поскольку JSONон реализован в собственном коде (в большинстве браузеров), это будет значительно быстрее, чем при использовании любого другого решения глубокого копирования на основе javascript, и иногда может быть быстрее, чем метод поверхностного копирования на основе javascript (см. Jsperf.com/cloning). -ан-объект / 79 ).
MiJyn 4.07.2013 07:42:37
JSON.stringify({key: undefined}) //=> "{}"
Web_Designer 30.04.2014 06:24:36
эта техника уничтожит также все Dateобъекты, которые хранятся внутри объекта, преобразовав их в строковую форму.
fstab 21.08.2014 12:51:24
Он не сможет скопировать что-либо, что не является частью спецификации JSON ( json.org )
cdmckay 9.02.2016 15:07:33

Структурированное клонирование

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

Поддержка в Node.js: экспериментальная

387
5.02.2019 02:21:46
@rynah Я только что посмотрел через спецификацию снова , и вы правы: history.pushState()и history.replaceState()методы как синхронно установить history.stateв структурированном клон первого аргумента. Немного странно, но это работает. Я обновляю свой ответ сейчас.
Jeremy Banks 6.05.2013 03:11:03
Это так неправильно! Этот API не предназначен для использования таким образом.
Fardin K. 31.07.2014 23:34:51
Как парень, который реализовал pushState в Firefox, я чувствую странную смесь гордости и отвращения к этому хаку. Молодцы ребята.
Justin L. 14.08.2014 18:37:25
Взлом pushState или Notification не работает для некоторых типов объектов, таких как Function
Shishir Arora 3.07.2019 20:06:56

Если бы не было встроенного, вы можете попробовать:

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;
}
322
27.09.2018 08:59:03
Решение JQuery будет работать для элементов DOM, а не только для любого объекта. Mootools имеет тот же предел. Хотелось бы, чтобы у них был общий «клон» для любого объекта ... Рекурсивное решение должно работать на все. Это, вероятно, путь.
jschrab 23.09.2008 17:23:51
Эта функция прерывается, если клонируемый объект имеет конструктор, который требует параметров. Кажется, что мы можем изменить его на "var temp = new Object ()" и заставить его работать в каждом случае, нет?
Andrew Arnott 4.10.2009 22:06:39
Эндрю, если вы измените его на var temp = new Object (), то ваш клон не будет иметь тот же прототип, что и исходный объект. Попробуйте использовать: 'var newProto = function () {}; newProto.prototype = obj.constructor; var temp = new newProto (); '
limscoder 14.09.2011 15:53:31
Аналогично ответу limscoder, смотрите мой ответ ниже о том, как сделать это без вызова конструктора: stackoverflow.com/a/13333781/560114
Matt Browne 11.11.2012 17:55:03
Для объектов, которые содержат ссылки на части (например, сети объектов), это не работает: если две ссылки указывают на один и тот же подобъект, копия содержит две разные его копии. И если есть рекурсивные ссылки, функция никогда не завершится (ну, по крайней мере, не так, как вы этого хотите :-) Для этих общих случаев вы должны добавить словарь уже скопированных объектов и проверить, скопировали ли вы его уже ... Программирование сложно, если вы используете простой язык
virtualnobi 11.11.2013 08:34:37

Эффективный способ клонирования (не глубокого клонирования) объекта в одну строку кода

Object.assignМетод является частью 2015 (ES6) стандарта ECMAScript и делает именно то , что вам нужно.

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;
    }
  });
}
154
14.02.2017 08:46:58
Это не рекурсивное копирование, поэтому не предлагает решения проблемы клонирования объекта.
mwhite 8.03.2016 19:56:53
Этот метод работал, хотя я тестировал несколько и _.extend ({}, (obj)) был на порядок быстрее, чем в 20 раз быстрее, чем JSON.parse и на 60% быстрее, чем Object.assign, например. Он хорошо копирует все подобъекты.
Nico 9.05.2016 19:57:22
@ белый, есть разница между клоном и глубоким клоном. Этот ответ фактически клонирует, но не глубоко клонирует.
Meirion Hughes 8.06.2016 12:08:03
Оператор попросил глубокого клона. это не делает глубокий клон.
user566245 31.07.2016 03:49:03
Этот метод делает ДОЛЖНУЮ копию , а не ГЛУБИНУЮ копию ! Из-за этого это совершенно неправильный ответ !
Bharata 6.09.2018 19:59:34

Код:

// 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);
96
26.03.2011 14:20:09
что о var obj = {}иobj.a = obj
neaumusic 5.05.2015 22:40:00
Я не понимаю эту функцию. Предположим from.constructor, Dateк примеру. Как будет ifдостигнут третий тест, если второй ifтест будет успешным и вызовет возврат функции (с тех пор Date != Object && Date != Array)?
Adam McKee 1.09.2015 02:10:41
@AdamMcKee Потому что передача аргументов javascript и назначение переменных довольно сложны . Этот подход прекрасно работает, в том числе даты (которые действительно обрабатываются вторым тестом) - fiddle для тестирования здесь: jsfiddle.net/zqv9q9c6 .
brichins 14.06.2016 20:39:15
* Я имел в виду «назначение параметров» (в теле функции), а не «назначение переменных». Хотя это помогает понять оба тщательно.
brichins 15.06.2016 16:42:40
@NickSweeting: Попробуйте - может быть, это работает. Если нет - исправьте и обновите ответ. Вот как это работает здесь, в сообществе :)
Kamarey 10.07.2017 07:20:58

Это то, что я использую:

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;
}
95
16.07.2013 16:37:34
Это не кажется правильным. cloneObject({ name: null })=>{"name":{}}
Niyaz 27.02.2013 14:36:45
Это связано с еще одной глупостью в javascript, typeof null > "object"но Object.keys(null) > TypeError: Requested keys of a value that is not an object.измените условие наif(typeof(obj[i])=="object" && obj[i]!=null)
Vitim.us 16.04.2013 16:42:45
Это назначит наследуемые перечисляемые свойства объекта obj непосредственно клону и предполагает, что объект obj является простым объектом.
RobG 1.02.2016 22:58:11
Это также портит массивы, которые преобразуются в объекты с помощью цифровых клавиш.
blade 3.11.2016 12:29:03
Не проблема, если вы не используете нуль.
Jorge Bucaran 1.03.2017 06:17:44

Глубокое копирование по производительности: от лучшего к худшему

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

Глубокая копия массива строк или чисел (один уровень - без ссылочных указателей):

Когда массив содержит числа и строки - такие функции, как .slice (), .concat (), .splice (), оператор присваивания "=" и функция клона 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 имеет лучшую производительность:

77
20.03.2018 15:11:04
Я протестировал несколько из них, и _.extend ({}, (obj)) был на BY FAR самым быстрым: в 20 раз быстрее, чем JSON.parse и на 60% быстрее, чем Object.assign, например. Он хорошо копирует все подобъекты.
Nico 9.05.2016 19:56:02
Все ваши примеры мелкие, один уровень. Это не хороший ответ. Вопрос касался глубокого клонирования, т.е. как минимум двух уровней.
Karl Morrison 21.04.2017 10:02:14
Глубокая копия - это когда объект копируется полностью без использования ссылочных указателей на другие объекты. Методы в разделе «Глубокое копирование массива объектов», такие как jQuery.extend () и пользовательская функция (которая является рекурсивной), копируют объекты с «минимум двумя уровнями». Итак, нет, не все примеры являются «одноуровневыми» копиями.
tfmontague 21.04.2017 15:42:01
Мне нравится ваша пользовательская функция копирования, но вы должны исключить нулевые значения, в противном случае все нулевые значения преобразуются в объекты, то есть:out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
josi 1.02.2018 16:53:21
@HossamMourad - ошибка была исправлена ​​Josi 1 февраля (в комментарии выше), и мне не удалось правильно обновить ответ. Извините, что эта ошибка привела к рефакторингу вашей кодовой базы.
tfmontague 20.03.2018 15:29:57
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});
64
21.02.2013 15:01:15
Хороший ответ, но это не подходит для циклических ссылок.
Luke 8.08.2019 16:05:08

Глубокое копирование объектов в JavaScript (я думаю, лучший и самый простой)

1. Использование JSON.parse (JSON.stringify (object));

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 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.b.c = 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.b.c = 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.b.c = 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.b.c = 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 Performance Benchmarking Playground 1 ~ 3 http://jsben.ch/KVQLd Производительность Глубокое копирование объектов в JavaScript

58
20.11.2019 04:52:48
Object.assign()не выполняет глубокую копию
Roymunson 15.08.2018 20:23:28
Вы должны добавить тесты для них; что было бы очень полезно
jcollum 30.10.2018 23:00:19
когда я использовал «созданный метод» для объекта, содержащего массив, я не смог использовать pop () или splice (), я не понимаю, почему? let data = {title:["one", "two"]}; let tmp = cloneObject(data); tmp.title.pop();он бросает: TypeError: tmp.title.pop is not a function(конечно, pop () работает нормально, если я просто do let tmp = data; но тогда я не могу изменить tmp, не влияя на данные)
hugogogo 24.03.2019 23:54:35
Эй, твой последний пример неверен. На мой взгляд, вы должны использовать _clone, а не _cloneDeep для неправильного примера.
kenanyildiz 10.12.2019 07:20:58
Этот созданный метод (2.) не будет работать для массивов, не так ли?
Toivo Säwén 18.12.2019 14:06:07

Есть библиотека (называемая «клон») , которая делает это довольно хорошо. Он обеспечивает наиболее полное рекурсивное клонирование / копирование произвольных известных мне объектов. Он также поддерживает циклические ссылки, которые пока не охвачены другими ответами.

Вы также можете найти его на npm . Может использоваться как для браузера, так и для Node.js.

Вот пример того, как его использовать:

Установите его с

npm install clone

или упакуй это с Эндером .

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

(Отказ от ответственности: я автор библиотеки.)

57
27.03.2013 08:04:54
Клон npm был неоценим для меня за клонирование произвольно вложенных объектов. Это правильный ответ.
Andy Ray 10.09.2015 03:21:23
какова производительность вашей библиотеки по сравнению с скажем JSON.parse(JSON.stringify(obj))?
pkyeck 1.12.2016 16:03:35
Вот библиотека, которая утверждает, что есть более быстрые варианты. Не проверял все же.
pvorb 1.12.2016 21:07:34
Хорошее решение, и это поддерживает циклические ссылки (в отличие от анализа JSON)
Luke 8.08.2019 16:08:36

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) Подчеркиваем и загружаем:

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

Надеюсь, что это поможет ...

54
24.02.2018 17:35:02
клон в подчеркивании не является глубоким клоном в текущей версии
Rogelio 3.04.2017 16:19:25
Спасибо. да, как новый документ для Underscore ... clone_.clone (объект) Создать мелко скопированный клон предоставленного простого объекта. Любые вложенные объекты или массивы будут скопированы по ссылке, а не скопированы. _.clone ({name: 'moe'}); => {name: 'moe'};
Alireza 4.04.2017 01:00:44
Object.assignэто не глубокая копия. Пример: var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d". Если бы это была глубокая копия, y.a.bвсе равно было бы c, но это сейчас d.
kba 10.05.2017 15:52:55
Object.assign () клонирует только первый уровень свойств!
haemse 21.07.2017 10:13:53
что такое функция cloneSO ()?
pastorello 7.08.2017 16:55:40

Я знаю, что это старый пост, но я подумал, что это может помочь следующему человеку, который спотыкается.

Пока вы не назначаете объект чему-либо, он не сохраняет ссылки в памяти. Таким образом, чтобы создать объект, которым вы хотите поделиться с другими объектами, вам нужно создать фабрику следующим образом:

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);
53
17.03.2015 01:18:29
Этот ответ на самом деле не имеет отношения к делу, поскольку вопрос таков: для данного экземпляра b как создать копию c, не зная о фабрике a или не желая использовать фабрику a. Причина, по которой человек может не захотеть использовать фабрику, заключается в том, что после создания экземпляра b могут быть инициализированы дополнительные данные (например, пользовательский ввод).
Noel Abrahams 8.05.2012 09:57:53
Это правда, что на самом деле это не ответ на вопрос, но я думаю, что важно быть здесь, потому что это ответ на вопрос, который, как я подозреваю, многие из приезжающих сюда действительно хотят задать.
Semicolon 6.03.2013 23:31:03
Извините, ребята, я не очень понимаю, почему так много голосов. Клонирование объекта - довольно ясная концепция, вы связываете объект от ДРУГОГО объекта, и это не имеет большого отношения к созданию нового с фабричным шаблоном.
opensas 18.08.2014 12:51:28
Хотя это работает для предопределенных объектов, «клонирование» таким способом не распознает новые свойства, добавленные к исходному объекту. Если вы создаете a, добавляете новое свойство в a, затем создаете b. б не будет иметь нового свойства. По сути, фабричный образец является неизменным для новых свойств. Это не парадигматически клонировать. См: jsfiddle.net/jzumbrun/42xejnbx
Jon 3.09.2016 13:59:46
Я думаю, что это хороший совет, так как вместо использования const defaultFoo = { a: { b: 123 } };вы можете пойти const defaultFoo = () => ({ a: { b: 123 } };и ваша проблема решена. Тем не менее, это действительно не ответ на вопрос. Это могло бы иметь больше смысла как комментарий к вопросу, а не как полный ответ.
Josh from Qaribou 6.02.2017 14:25:41

Если вы используете его, в библиотеке Underscore.js есть метод clone .

var newObject = _.clone(oldObject);
48
22.07.2016 17:26:12
У lodash есть метод cloneDeep, он также поддерживает другой параметр для клонирования, чтобы углубить его: lodash.com/docs#clone и lodash.com/docs#cloneDeep
opensas 2.03.2013 17:14:53
@opensas согласился. Lodash, как правило, лучше подчеркивания
nha 31.07.2014 12:12:37
Я защищаю удаление этого и всех других ответов, которые являются только однострочными ссылками на .clone(...)метод служебной библиотеки . Они есть в каждой крупной библиотеке, а повторяющиеся краткие, не детализированные ответы бесполезны для большинства посетителей, которые не будут использовать эту конкретную библиотеку.
Jeremy Banks 7.04.2016 17:25:25

Вот версия ответа 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 .

Редактировать:

Вот более надежная версия (благодаря Джастину Маккэндлессу теперь она также поддерживает циклические ссылки):

/**
 * 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();
    };
}
41
12.03.2017 18:02:45

Следующее создает два экземпляра одного и того же объекта. Я нашел это и использую это в настоящее время. Это просто и легко в использовании.

var objToCreate = JSON.parse(JSON.stringify(cloneThis));
30
1.12.2016 16:06:53
Что-то не так с этим ответом? Это более полезно, поскольку является самостоятельным решением, но простым; но решение jQuery более популярно. Это почему?
ceremcem 24.09.2015 17:15:39
Да, пожалуйста, дайте мне знать. Похоже, что работает как задумано, если где-то есть скрытые поломки, мне нужно использовать другое решение.
nathan rogers 25.09.2015 15:34:58
Для простого объекта это примерно в 6 раз медленнее в Chrome, чем данный ответ, и становится намного медленнее по мере роста сложности объекта. Он ужасно масштабируется и может очень быстро ограничить ваше приложение.
tic 8.12.2015 23:10:37
Вам не нужны данные, просто понимание того, что происходит. Этот метод клонирования сериализует весь объект в строку, а затем анализирует сериализацию этой строки для построения объекта. По сути, это будет намного медленнее, чем просто реорганизация памяти (что делают более сложные клоны). Но с учетом вышесказанного, для небольших и средних проектов (в зависимости от вашего определения «среднего размера») кого волнует, будет ли он даже в 1000 раз менее эффективным? Если ваши объекты маленькие, и вы не клонируете их, тонна 1000х практически ничего - все равно практически ничто.
machineghost 19.12.2016 20:20:26
Кроме того, этот метод теряет методы (или любые вещи, которые не разрешены в JSON), плюс - JSON.stringify преобразует объекты Date в строки, ... и не наоборот;) Не используйте это решение.
Mr MT 11.08.2017 22:11:09

Крокфорд предлагает (и я предпочитаю) использовать эту функцию:

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

var newObject = object(oldObject);

Это вкратце, работает как положено, и вам не нужна библиотека.


РЕДАКТИРОВАТЬ:

Это 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
22
12.03.2016 14:59:41
поправьте меня, если я ошибаюсь, но разве эта функция Крокфорда не вызывает наследование прототипа? Как это относится к клону?
Alex Nolasco 6.10.2010 15:17:18
Да, я боялся этого обсуждения: какова практическая разница между клонированием, копированием и наследованием прототипа, когда вы должны использовать каждый из них и какие функции на этой странице фактически делают, что? Я нашел эту страницу SO, погуглив "javascript copy object". То, что я действительно искал, было функцией выше, поэтому я вернулся, чтобы поделиться. Я предполагаю, что аскер тоже искал это.
Chris Broski 6.10.2010 19:51:48
Разница между клоном / копией и наследованием заключается в том, что - на вашем примере, когда я изменяю свойство oldObject, свойство также изменяется в newObject. Если вы делаете копию, вы можете делать то, что вы хотите с oldObject, не меняя newObject.
Ridcully 6.12.2010 13:13:26
Это нарушит проверку hasOwnProperty, так что это довольно хакерский способ клонирования объекта и даст вам неожиданные результаты.
Corban Brook 16.03.2011 18:17:18
var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; };... davidshariff.com/blog/javascript-inheritance-patterns
Cody 24.04.2014 18:32:03

У Lodash есть хороший метод _.cloneDeep (value) :

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
22
31.12.2017 12:59:40
Я защищаю удаление этого и всех других ответов, которые являются только однострочными ссылками на .clone(...)метод служебной библиотеки . Они есть в каждой крупной библиотеке, а повторяющиеся краткие, не детализированные ответы бесполезны для большинства посетителей, которые не будут использовать эту конкретную библиотеку.
Jeremy Banks 7.04.2016 17:23:49
Более простой способ - использовать _.merge({}, objA). Если бы только lodash не мутировал объекты в первую очередь, тогда cloneфункция была бы не нужна.
Rebs 27.02.2017 00:48:02
Поиск Google для клонирования объектов JS см. Здесь. Я использую Lodash, поэтому этот ответ важен для меня. Давайте не пойдем все "Википедия делеционист" на ответы, пожалуйста.
Rebs 27.02.2017 00:49:11
В узле 9 JSON.parse (JSON.stringify (arrayOfAbout5KFlatObjects)) намного быстрее, чем _.deepClone (arrayOfAbout5KFlatObjects).
Dan Dascalescu 31.12.2017 13:07:15
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }
21
23.09.2008 16:45:39
Проблема с методом в том, что если у вас есть подчиненные объекты в объекте obj, их ссылки будут клонированы, а не значения каждого подобъекта.
Kamarey 25.06.2009 07:46:29
просто сделайте его рекурсивным, чтобы вспомогательные объекты были глубоко клонированы.
fiatjaf 25.01.2013 22:38:29
просто любопытно ... не будет ли переменная clone иметь указатели на свойства исходного объекта? потому что, кажется, нет нового выделения памяти
Rupesh Patel 4.02.2013 09:42:17
Да. Это просто поверхностная копия, поэтому клон будет указывать на те же самые объекты, на которые указывает исходный объект.
Mark Cidade 4.02.2013 10:18:09
Это не ответ. Вы буквально просто наполняете объект ссылками на другой объект. Внесение изменений в исходный объект внесет изменения в «клон».
Shawn Whinnery 20.03.2019 17:34:58

Однострочная копия мелкой копии ( 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
19
22.07.2016 17:27:51
Это может быть хорошо для простых объектов, но копирует только значения свойств. Он не касается цепочки прототипов и при использовании Object.keysпропускает не перечисляемые и унаследованные свойства. Кроме того, он теряет дескрипторы свойств, выполняя прямое присваивание.
Matt Bierner 23.11.2013 17:51:52
Если вы тоже скопируете прототип, вам не хватит только не перечисляемых и дескрипторов свойств, да? Довольно хорошо. :)
sam 8.06.2014 06:24:04
Помимо производительности, это действительно удобный способ поверхностного копирования объекта. Я часто использую это для сортировки ложных свойств покоя в назначении деструктурирования в моих компонентах React.
mjohnsonengr 17.03.2016 13:56:48

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

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

17
15.10.2016 18:38:31
или его можно использовать так же, как jQiery extension:angular.extend({},obj);
Galvani 21.09.2016 09:07:42
@Galvani: Следует отметить, что jQuery.extendи angular.extendоба являются мелкими копиями. angular.copyэто глубокая копия.
Dan Atkinson 15.10.2016 18:41:00

Кажется, еще нет идеального оператора глубокого клонирования для массивоподобных объектов. Как показано в приведенном ниже коде, клонер jQuery Джона Резига превращает массивы с нечисловыми свойствами в объекты, которые не являются массивами, а JSON-клонер RegDwight удаляет нечисловые свойства. Следующие тесты иллюстрируют эти пункты в нескольких браузерах:

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)
16
13.12.2013 19:00:03
как другие отмечали в комментариях к ответу Ресига, если вы хотите клонировать массивоподобный объект, вы меняете {} на [] в вызове расширения, например, jQuery.extend (true, [], obj)
Anentropic 3.03.2011 21:27:05

У меня есть два хороших ответа в зависимости от того, является ли ваша цель клонировать «простой старый объект JavaScript» или нет.

Предположим также, что вы намереваетесь создать законченный клон без ссылок прототипов на исходный объект. Если вас не интересует полный клон, вы можете использовать многие из подпрограмм Object.clone (), представленных в некоторых других ответах (шаблон Крокфорда).

Для простых старых объектов JavaScript надежный и надежный способ клонирования объекта в современных средах выполнения довольно прост:

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

Обратите внимание, что исходный объект должен быть чистым объектом JSON. То есть все его вложенные свойства должны быть скалярами (например, логическое значение, строка, массив, объект и т. Д.). Любые функции или специальные объекты, такие как RegExp или Date, не будут клонированы.

Это эффективно? Черт возьми, да. Мы перепробовали все виды методов клонирования, и это работает лучше всего. Я уверен, что какой-нибудь ниндзя мог бы придумать более быстрый метод. Но я подозреваю, что мы говорим о предельной прибыли.

Этот подход просто прост и легко реализуем. Оберните его в удобную функцию, и если вам действительно нужно выжать некоторую выгоду, сделайте это позже.

Теперь для непростых объектов JavaScript не существует простого ответа. Фактически, это не может быть из-за динамической природы функций JavaScript и состояния внутреннего объекта. Глубокое клонирование структуры JSON с функциями внутри требует повторного создания этих функций и их внутреннего контекста. И у JavaScript просто нет стандартизированного способа сделать это.

Правильный способ сделать это, опять же, через удобный метод, который вы объявляете и повторно используете в своем коде. Удобный метод может быть наделен некоторым пониманием ваших собственных объектов, чтобы вы могли убедиться, что правильно воссоздали график в новом объекте.

Мы написаны по-своему, но лучший общий подход, который я видел, описан здесь:

http://davidwalsh.name/javascript-clone

Это правильная идея. Автор (Дэвид Уолш) прокомментировал клонирование обобщенных функций. Это то, что вы можете выбрать, в зависимости от вашего варианта использования.

Основная идея заключается в том, что вам нужно специально обрабатывать создание ваших функций (или, так сказать, прототипов) для каждого типа. Здесь он предоставил несколько примеров для RegExp и Date.

Этот код не только краткий, но и очень читаемый. Это довольно легко расширить.

Это эффективно? Черт возьми, да. Учитывая, что цель состоит в том, чтобы создать настоящий клон глубокой копии, вам придется пройтись по элементам графа исходного объекта. При таком подходе вы можете настроить, какие дочерние элементы обрабатывать, и как вручную обрабатывать пользовательские типы.

Итак, поехали. Два подхода. Оба эффективны, на мой взгляд.

15
22.07.2016 17:33:57

Как правило, это не самое эффективное решение, но оно делает то, что мне нужно. Простые тестовые случаи ниже ...

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
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false
13
3.04.2011 02:08:03

AngularJS

Ну, если вы используете угловой, вы могли бы сделать это тоже

var newObject = angular.copy(oldObject);
11
14.09.2016 13:26:15

Я не согласен с ответом с наибольшим количеством голосов здесь . Рекурсивный Deep Clone это гораздо быстрее , чем 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
}
11
18.06.2017 06:34:37
Мне понравился этот подход, но он не обрабатывает даты должным образом; рассмотрите возможность добавления чего-то вроде if(o instanceof Date) return new Date(o.valueOf());после проверки на null `
Luis 21.08.2017 22:53:25
Сбои в циклических ссылках.
Harry 18.03.2018 05:19:50
В последнем стабильном Firefox это намного дольше, чем другие стратегии на этой ссылке Jsben.ch, на порядок или более. Это бьет других в неправильном направлении.
WBT 14.01.2019 18:38:35
// obj target object, vals source object
var setVals = function (obj, vals) {
    if (obj && vals) {
        for (var x in vals) {
            if (vals.hasOwnProperty(x)) {
                if (obj[x] && typeof vals[x] === 'object') {
                    obj[x] = setVals(obj[x], vals[x]);
                } else {
                    obj[x] = vals[x];
                }
            }
        }
    }
    return obj;
};
11
17.01.2018 16:27:11

Только тогда, когда вы можете использовать ECMAScript 6 или транспортеры .

Особенности:

  • Не будет вызывать геттер / сеттер при копировании.
  • Сохраняет геттер / сеттер.
  • Сохраняет прототип информации.
  • Работает как с объектно-буквальным, так и с функциональным стилем записи ОО .

Код:

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;
}
9
22.07.2016 17:46:50

Для людей, которые хотят использовать 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(x), function(k, v) {
    if (typeof v === 'string' && regExp.test(v))
      return new Date(v);
    return v;
  });
}
9
22.07.2016 17:47:24