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

вопрос создан в Wed, Mar 22, 2017 12:00 AM

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

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

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

    
5087
  1. Eval не является злом. Использование Eval плохо это. Если вы боитесь его побочных эффектов, вы используете его неправильно. Побочные эффекты, которых вы боитесь, являются причинами его использования. Кто-нибудь, кстати, на самом деле ответил на ваш вопрос?
    2012-03-22 14: 08: 54Z
  2. Клонирование объектов - сложная задача, особенно с пользовательскими объектами произвольных коллекций. Который, вероятно, почему нет готового способа сделать это.
    2013-03-11 22: 25: 34Z
  3. eval(), как правило, плохая идея, потому что многие оптимизаторы движка Javascript должны отключаться при работе с переменными, которые установлены через eval . Наличие eval() в вашем коде может привести к снижению производительности.
    2014-09-08 13: 37: 45Z
  4. 2016-02-21 18: 21: 08Z
  5. Обратите внимание, что метод 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}
    2017-05-24 13: 06: 47Z
30 ответов                              30                         
  

Примечание за 2019 год - июнь. Первоначально это был ответ на другой, а не правильный ответ на этот вопрос. Не знаю, почему он был выбран как правильный ответ. Но так как возмущение возросло, и это, безусловно, ответ № 1 на этот вопрос, он суммирует решения в виде ответа вики.

Нативное глубокое клонирование

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

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

Если вы не используете Date с, функции, undefined, Infinity, RegExps, Карты, Наборы, BLOB-объекты, Списки файлов, 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()

См. Ответ Корбана о тестах.

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

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

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

ES6

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

4311
2019-06-14 02: 29: 00Z
  1. @ ThiefMaster github.com/jquery/jquery/blob/master/src/core.js в строке 276 (есть немного кода, который делает что-то еще, но есть код для «как сделать это в JS»: )
    2013-03-27 08: 16: 19Z
  2. Вот код JS, стоящий за глубокой копией jQuery, для всех, кто заинтересован: github.com/jquery/jquery/blob/master/src/core.js#L265-327
    2013-04-11 14: 24: 13Z
  3. Вау! Просто чтобы быть предельно ясным: не знаю, почему этот ответ был выбран как правильный ответ, это был ответ на ответы, приведенные ниже: stackoverflow. com /a /122190/6524 (который рекомендовал .clone(), что не является правильным кодом для использования в этом контексте). К сожалению, этот вопрос подвергся стольким пересмотрам, что первоначальная дискуссия уже не очевидна! Пожалуйста, просто следуйте советам Корбана и напишите цикл или скопируйте свойства непосредственно в новый объект, если вам нужна скорость. Или проверьте это сами!
    2014-01-21 03: 37: 38Z
  4. Это вопрос JavaScript (без упоминания jQuery).
    2015-01-22 08: 30: 39Z
  5. Как можно это сделать без использования jQuery?
    2015-04-30 02: 22: 57Z

Проверьте этот тест: 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.

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

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

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

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

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

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

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

 
Object.assign({}, obj);
    

2179
2019-06-21 11: 52: 59Z
  1. @ trysis Object.create не клонирует объект, использует объект-прототип ... jsfiddle.net/rahpuser/yufzc1jt/2
    2014-11-25 21: 37: 12Z
  2. Этот метод также удалит keys из вашего object, у которого functions в качестве значений, потому что JSON не поддерживает функции.
    2015-05-29 09: 52: 16Z
  3. Также имейте в виду, что использование JSON.parse(JSON.stringify(obj)) для объектов Date также преобразует дату обратно в UTC в строковом представлении в ISO8601. формат.
    2015-07-30 21: 37: 10Z
  4. Подход JSON также подавляет циклические ссылки.
    2016-02-13 05: 25: 15Z
  5. @ velop, Object.assign ({}, objToClone), хотя кажется, что он делает мелкий клон - используя его во время игры в консоли инструментов dev, объект клон по-прежнему указывает на ссылку на клонированный объект. Поэтому я не думаю, что это действительно применимо здесь.
    2016-11-03 18: 00: 14Z

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

 
var newObject = JSON.parse(JSON.stringify(oldObject));
    
452
2014-10-23 23: 47: 23Z
  1. , как я только что обнаружил, этот подход состоит в том, что если у вашего объекта есть какие-либо функции (у меня есть внутренние методы получения и установки), то они теряются при строковом преобразовании .. Если это все, что вам нужно, этот метод подойдет ..
    2013-01-17 11: 00: 08Z
  2. @ Jason, причина, по которой этот метод медленнее, чем поверхностное копирование (для глубокого объекта), заключается в том, что этот метод по определению является глубоким копированием. Но так как JSON реализован в собственном коде (в большинстве браузеров), это будет значительно быстрее, чем при использовании любого другого решения глубокого копирования на основе JavaScript, и может иногда быть быстрее, чем метод поверхностного копирования на основе JavaScript (см .: jsperf.com/cloning-an-object/79 ).
    2013-07-04 07: 42: 37Z
  3. JSON.stringify({key: undefined}) //=> "{}"
    2014-04-30 06: 24: 36Z
  4. этот метод уничтожит также все Date объектов, которые хранятся внутри объекта, преобразовав их в строковую форму.
    2014-08-21 12: 51: 24Z
  5. Не удастся скопировать все, что не является частью спецификации JSON ( json.org )
    2016-02-09 15: 07: 33Z

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

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

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

Модуль v8 в Node.js в настоящее время (по состоянию на Узел 11) напрямую предоставляет API структурированной сериализации. , но эта функция по-прежнему помечена как «экспериментальная» и может быть изменена или удалена в будущих версиях. Если вы используете совместимую версию, клонирование объекта так же просто, как:

 
const v8 = require('v8');

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

Прямая поддержка в браузерах: возможно, в конце концов? 😐

Браузеры в настоящее время не предоставляют прямой интерфейс для алгоритма структурированного клонирования, но глобальная функция structuredClone() обсуждалась в whatwg /html # 793 на GitHub . Как предлагается в настоящее время, использовать его для большинства целей так же просто, как:

 
const clone = structuredClone(original);

Если это не сделано, реализации структурированных клонов в браузерах предоставляются только косвенно.

Асинхронный обходной путь: можно использовать. 😕

Более простой способ создания структурированного клона с существующими API-интерфейсами заключается в публикации данных через один порт MessageChannels . Другой порт будет генерировать событие message со структурированным клоном подключенного .data. К сожалению, прослушивание этих событий обязательно асинхронно, а синхронные альтернативы менее практичны.

 
class StructuredCloner {
  constructor() {
    this.pendingClones_ = new Map();
    this.nextKey_ = 0;

    const channel = new MessageChannel();
    this.inPort_ = channel.port1;
    this.outPort_ = channel.port2;

    this.outPort_.onmessage = ({data: {key, value}}) => {
      const resolve = this.pendingClones_.get(key);
      resolve(value);
      this.pendingClones_.delete(key);
    };
    this.outPort_.start();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);

Пример использования:

 
const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

main();

Синхронные обходные пути: ужасно! 🤢

Нет хороших вариантов для синхронного создания структурированных клонов. Вот пара непрактичных взломов.

history.pushState() и history.replaceState() оба создают структурированный клон своего первого аргумента и присваивают это значение history.state. Вы можете использовать это для создания структурированного клона любого объекта, подобного этому:

 
const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

Пример использования:

р>

 
'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

main();

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

конструктор

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

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

 Notification

Пример использования:

р>

 
const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.onshow = n.close.bind(n);
  return n.data;
};
    
335
2019-02-05 02: 21: 46Z
  1. @ rynah Я только что снова просмотрел спецификацию, и вы правы: оба метода
    'use strict';
    
    const main = () => {
      const original = { date: new Date(), number: Math.random() };
      original.self = original;
    
      const clone = structuredClone(original);
      
      // They're different objects:
      console.assert(original !== clone);
      console.assert(original.date !== clone.date);
    
      // They're cyclical:
      console.assert(original.self === original);
      console.assert(clone.self === clone);
    
      // They contain equivalent values:
      console.assert(original.number === clone.number);
      console.assert(Number(original.date) === Number(clone.date));
      
      console.log("Assertions complete.");
    };
    
    const structuredClone = obj => {
      const n = new Notification('', {data: obj, silent: true});
      n.close();
      return n.data;
    };
    
    main();
    и
    'use strict';
    
    const main = () => {
      const original = { date: new Date(), number: Math.random() };
      original.self = original;
    
      const clone = structuredClone(original);
      
      // They're different objects:
      console.assert(original !== clone);
      console.assert(original.date !== clone.date);
    
      // They're cyclical:
      console.assert(original.self === original);
      console.assert(clone.self === clone);
    
      // They contain equivalent values:
      console.assert(original.number === clone.number);
      console.assert(Number(original.date) === Number(clone.date));
      
      console.log("Assertions complete.");
    };
    
    const structuredClone = obj => {
      const n = new Notification('', {data: obj, silent: true});
      n.close();
      return n.data;
    };
    
    main();
    синхронно устанавливают history.pushState() в структурированный клон своего первого аргумента. Немного странно, но это работает. Я обновляю свой ответ сейчас.
    2013-05-06 03: 11: 03Z
  2. Это просто так неправильно! Этот API не предназначен для использования таким образом.
    2014-07-31 23: 34: 51Z
  3. Как парень, который реализовал pushState в Firefox, я чувствую странную смесь гордости и отвращения к этому хаку. Молодцы, ребята.
    2014-08-14 18: 37: 25Z

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

 history.replaceState()     
309
2018-09-27 08: 59: 03Z
  1. Решение JQuery будет работать для элементов DOM, а не только для любого объекта. Mootools имеет тот же предел. Хотелось бы, чтобы у них был общий «клон» для любого объекта ... Рекурсивное решение должно работать для всего. Это, вероятно, путь.
    2008-09-23 17: 23: 51Z
  2. Эта функция прерывается, если клонируемый объект имеет конструктор, который требует параметров. Кажется, мы можем изменить его на "var temp = new Object ()" и заставить его работать в каждом случае, нет?
    2009-10-04 22: 06: 39Z
  3. Эндрю, если вы измените его на var temp = new Object (), то у вашего клона не будет такого же прототипа, как у исходного объекта. Попробуйте использовать: 'var newProto = function () {}; newProto.prototype = obj.constructor; var temp = new newProto (); '
    2011-09-14 15: 53: 31Z
  4. Аналогично ответу limscoder, смотрите мой ответ ниже о том, как это сделать без вызова конструктора: stackoverflow.com/a/13333781/560114
    2012-11-11 17: 55: 03Z
  5. Для объектов, которые содержат ссылки на части (т. е. сети объектов), это не работает: если две ссылки указывают на один и тот же подобъект, копия содержит две разные копии этого. И если есть рекурсивные ссылки, функция никогда не завершится (ну, по крайней мере, не так, как вы этого хотите :-) Для этих общих случаев вы должны добавить словарь уже скопированных объектов и проверить, скопировали ли вы его уже ... Программирование сложно, если вы используете простой язык
    2013-11-11 08: 34: 37Z

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

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

 
function clone(obj) {
    if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

    if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
    else
        var temp = obj.constructor();

    for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            obj['isActiveClone'] = null;
            temp[key] = clone(obj[key]);
            delete obj['isActiveClone'];
        }
    }
    return temp;
}
  

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

Подробнее ...

полифилл для поддержки старых браузеров:

 Object.assign     
154
2017-02-14 08: 46: 58Z
  1. Это не рекурсивное копирование, поэтому не предлагает решения проблемы клонирования объекта.
    2016-03-08 19: 56: 53Z
  2. Этот метод сработал, хотя я протестировал несколько из них, и _.extend ({}, (obj)) был на FAR самым быстрым: в 20 раз быстрее, чем JSON.parse и 60 % быстрее, чем Object.assign, например. Он хорошо копирует все подобъекты.
    2016-05-09 19: 57: 22Z
  3. @ mwhite существует разница между клоном и глубоким клоном. Этот ответ действительно клонирует, но не глубоко клонирует.
    2016-06-08 12: 08: 03Z
  4. операция запросила глубокое клонирование. это не делает глубокий клон.
    2016-07-31 03: 49: 03Z
  5. вау так много ответов Object.assign, даже не прочитав вопрос опер ...IV>
    2017-12-29 10: 02: 16Z

    код:

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

    Тест:

     
    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;
        }
      });
    }
    
        
    96
    2011-03-26 14: 20: 09Z
    1. как насчет
      // 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);
      
      2015-05-05 22: 40: 00Z
    2. Я не понимаю эту функцию. Предположим, var obj = {}, например, obj.a = obj. Как будет выполнен третий тест from.constructor, когда второй тест Date будет успешным & заставить функцию возвращаться (начиная с if)?
      2015-09-01 02: 10: 41Z
    3. @ AdamMcKee, потому что Передача аргументов javascript и назначение переменных довольно сложно . Этот подход прекрасно работает, в том числе даты (которые действительно обрабатываются вторым тестом) - проверить здесь: jsfiddle.net/zqv9q9c6 .
      2016-06-14 20: 39: 15Z
    4. * Я имел в виду "назначение параметров" (в теле функции), а не "назначение переменных". Хотя это помогает понять и то, и другое.
      2016-06-15 16: 42: 40Z
    5. @ NickSweeting: Попробуйте - возможно, это работает. Если нет - исправьте и обновите ответ. Вот как это работает здесь, в сообществе:)
      2017-07-10 07: 20: 58Z

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

     if     
    92
    2013-07-16 16: 37: 34Z
    1. Это кажется неправильным. Date != Object && Date != Array = >
      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;
      }
      
      2013-02-27 14: 36: 45Z
    2. Это происходит из-за еще одной глупости в javascript cloneObject({ name: null }), но {"name":{}} меняет условие на typeof null > "object"
      2013-04-16 16: 42: 45Z
    3. Это назначит унаследованные перечисляемые свойства obj непосредственно клону и предполагает, что obj является простым объектом .
      2016-02-01 22: 58: 11Z
    4. Это также приводит к путанице в массивах, которые преобразуются в объекты с числовыми ключами.
      2016-11-03 12: 29: 03Z
    5. Не проблема, если вы не используете null.
      2017-03-01 06: 17: 44Z

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

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

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

    Когда массив содержитчисла и строки - такие функции, как .slice (), .concat (), .splice (), оператор присваивания "=" и функция клона Underscore.js; создаст полную копию элементов массива.

    Где переназначение имеет самую быструю производительность:

     Object.keys(null) > TypeError: Requested keys of a value that is not an object.

    И .slice () имеет лучшую производительность, чем .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3 р>  if(typeof(obj[i])=="object" && obj[i]!=null)

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

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

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

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

    Где jQuery $.extend имеет лучшую производительность:

    77
    2018-03-20 15: 11: 04Z
    1. Я протестировал несколько тестов, и _.extend ({}, (obj)) был BY FAR самым быстрым: в 20 раз быстрее, чем JSON.parse, и на 60% быстрее, чем Object. назначить, например. Он хорошо копирует все подобъекты.
      2016-05-09 19: 56: 02Z
    2. @ NicoDurand - Ваши тесты производительности онлайн?
      2016-05-17 13: 36: 31Z
    3. Все ваши примеры поверхностные, одного уровня. Это не хороший ответ. Вопрос касался глубокого клонирования, то есть как минимум двух уровней.
      2017-04-21 10: 02: 14Z
    4. Глубокая копия - это когда объект копируется целиком без использования ссылочных указателей на другие объекты. Методы в разделе «Глубокое копирование массива объектов», такие как jQuery.extend () и пользовательская функция (которая является рекурсивной), копируют объекты с «минимум двумя уровнями». Так что нет, не все примеры являются «одноуровневыми» копиями.
      2017-04-21 15: 42: 01Z
    5. Мне нравится ваша пользовательская функция копирования, но вы должны исключить нулевые значения, в противном случае все нулевые значения преобразуются в объекты, т.е.
      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);
      
      2018-02-01 16: 53: 21Z
     
    $.extend(true, [], arr1); // Jquery Extend
    JSON.parse(arr1);
    _.cloneDeep(arr1); // Lo-dash
    
        
    62
    2013-02-21 15: 01: 15Z

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

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

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

    Установите его с помощью

     out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;

    или упакуйте его с помощью Ender .

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

    Вы также можете загрузить исходный код вручную.

    Затем вы можете использовать его в своем исходном коде.

     
    npm install clone
    

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

        
    55
    2013-03-27 08: 04: 54Z
    1. клон npm был неоценим для меня для клонирования произвольно вложенных объектов. Это правильный ответ.
      2015-09-10 03: 21: 23Z
    2. какова производительность вашей библиотеки по сравнениюскажем
      ender build clone [...]
      
      ?
      2016-12-01 16: 03: 35Z
    3. Вот библиотека в котором говорится, что есть более быстрые варианты. Хотя не проверял.
      2016-12-01 21: 07: 34Z

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

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

     
    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' } }
    
        
    52
    2015-03-17 01: 18: 29Z
    1. Этот ответ на самом деле не актуален, поскольку вопрос таков: для данного экземпляра b как создать копию c, НЕ зная о фабрике a или не желая использовать фабрику a. Причина, по которой человек может не захотеть использовать фабрику, заключается в том, что после создания экземпляра b могут быть инициализированы дополнительные данные (например, ввод пользователя).
      2012-05-08 09: 57: 53Z
    2. Это правда, что это не совсем ответ на вопрос, но я думаю, что важно быть здесь, потому что это ответ на вопрос, который я подозреваю, многие из люди, приезжающие сюда, действительно хотят спросить.
      2013-03-06 23: 31: 03Z
    3. Извините, ребята, я не очень понимаю, почему так много голосов. Клонирование объекта - довольно ясная концепция: вы создаете объект из ДРУГОГО объекта, и это не имеет ничего общего с созданием нового с фабричным шаблоном.
      2014-08-18 12: 51: 28Z
    4. Хотя это работает для предопределенных объектов, «клонирование» таким образом не распознает новые свойства, добавленные к исходному объекту. Если вы создаете a, добавляете новое свойство в a, затем создаете b. б не будет иметь нового свойства. По сути, фабричный образец является неизменным для новых свойств. Это не парадигматическое клонирование. См. jsfiddle.net/jzumbrun/42xejnbx
      2016-09-03 13: 59: 46Z
    5. Как правило, это хороший совет, поскольку вместо использования JSON.parse(JSON.stringify(obj)) вы можете перейти на
      var a = function(){
          return {
              father:'zacharias'
          };
      },
      b = a(),
      c = a();
      c.father = 'johndoe';
      alert(b.father);
      
      , и ваша проблема решена. Тем не менее, это действительно не ответ на вопрос. Это могло бы иметь больше смысла как комментарий к вопросу, а не как полный ответ.
      2017-02-06 14: 25: 41Z

    const defaultFoo = { a: { b: 123 } }; Объект всегда был проблемой в JS, но все это было до ES6, ниже я перечисляю различные способы копирования объекта в JavaScript, представьте, что у вас есть объект ниже и вы хотели бы иметь его глубокую копию: р>  const defaultFoo = () => ({ a: { b: 123 } };

    Есть несколько способов скопировать этот объект без изменения источника:

    1) ES5 +, используя простую функцию, чтобы сделать копию для вас:

     Cloning

    2) ES5 +, используя JSON.parse и JSON.stringify.

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

    3) AngularJs:

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

    4) jQuery:

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

    5) UnderscoreJs & Loadash:

     
    var  deepCopyObj = angular.copy(obj);
    

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

        
    52
    2018-02-24 17: 35: 02Z
    1. клон в подчеркивании не является глубоким клоном в текущей версии
      2017-04-03 16: 19: 25Z
    2. Спасибо. да, как новый документ для Underscore ... clone_.clone (объект) Создать мелко скопированный клон рровный объект Любые вложенные объекты или массивы будут скопированы по ссылке, а не скопированы. _.clone ({name: 'moe'}); = &GT; {name: 'moe'};
      2017-04-04 01: 00: 44Z
    3. var deepCopyObj = jQuery.extend(true, {}, obj);
      
      не выполняет не глубокое копирование. Пример:
      var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy
      
      . Если бы это была глубокая копия, Object.assign все равно был бы var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d", но теперь это y.a.b.
      2017-05-10 15: 52: 55Z
    4. Object.assign () клонирует только первый уровень свойств!
      2017-07-21 10: 13: 53Z
    5. что такое функция cloneSO ()?
      2017-08-07 16: 55: 40Z

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

     c     
    48
    2016-07-22 17: 26: 12Z
    1. В lodash есть метод cloneDeep, он также поддерживает другой параметр для клонирования, чтобы углубить его: lodash.com/docs#clone и lodash.com/docs#cloneDeep
      2013-03-02 17: 14: 53Z
    2. @ открывается в соответствии с соглашением. Lodash, как правило, лучше подчеркивания
      2014-07-31 12: 12: 37Z
    3. Я рекомендую удалить этот и все другие ответы, которые являются однострочными ссылками на метод d служебной библиотеки. Они есть в каждой крупной библиотеке, а повторяющиеся краткие, не подробные ответы бесполезны для большинства посетителей, которые не будут использовать эту конкретную библиотеку.
      2016-04-07 17: 25: 25Z

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

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

     
    var newObject = _.clone(oldObject);
    

    2.Использование созданного метода

     .clone(...)

    3. Используя ссылку _.cloneDeep Lo-Dash lodash

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

    4. Использование метода Object.assign ()

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

    НО НЕПРАВИЛЬНО КОГДА

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

    5.Использование ссылки Underscore.js _.clone Underscore.js  

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

    НО НЕПРАВИЛЬНО КОГДА

     
    var obj = { 
      a: 1,
      b: { 
        c: 2
      }
    }
    
    var newObj = 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.
    

    Reference medium.com

    JSBEN.CH - игровая площадка для тестирования производительности 1 ~ 3 http://jsben.ch/KVQLd «Производительность

        
    43
    2018-10-31 01: 25: 49Z
    1. 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 }  
      
      не выполняет глубокую копию
      2018-08-15 20: 23: 28Z
    2. вы должны добавить тесты для них; это было бы очень полезно
      2018-10-30 23: 00: 19Z
    3. когда я использовал «созданный метод» для объекта, содержащего массив, я не смогиспользуйте pop () или splice (), я не понимаю почему?
      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.)
      
      это бросить: Object.assign() (конечно, pop () работает нормально, если я просто let data = {title:["one", "two"]}; let tmp = cloneObject(data); tmp.title.pop();; но тогда я не могу изменить tmp без влияния на данные)
      2019-03-24 23: 54: 35Z

    Ниже приведен вариант ответа ConroyP, который работает, даже если у конструктора есть обязательные параметры:

     TypeError: tmp.title.pop is not a function

    Эта функция также доступна в моей simpleoo библиотеке.

    Edit:

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

     do let tmp = data     
    38
    2017-03-12 18: 02: 45Z

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

     
    //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;
    }
    
        
    31
    2016-12-01 16: 06: 53Z
    1. Что-то не так с этим ответом? Это более полезно, поскольку является самостоятельным решением, но простым; но решение jQuery более популярно. Почему это так?
      2015-09-24 17: 15: 39Z
    2. Да, пожалуйста, дайте мне знать. Похоже, что работает как задумано, если где-то есть какой-то скрытый разрыв, мне нужно использовать другое решение.
      2015-09-25 15: 34: 58Z
    3. Для простого объекта в Chrome это примерно в 6 раз медленнее, чем данный ответ, и становится намного медленнее по мере роста сложности объекта. Он ужасно масштабируется и может очень быстро стать узким местом для вашего приложения.
      2015-12-08 23: 10: 37Z
    4. Вам не нужны данные, только понимание того, что происходит. Этот метод клонирования сериализует весь объект в строку, а затем анализирует сериализацию этой строки для построения объекта. По сути, это будет намного медленнее, чем просто реорганизация памяти (что делают более сложные клоны). Но с учетом вышесказанного, для небольших и средних проектов (в зависимости от вашего определения «среднего размера») кого волнует, будет ли он даже в 1000 раз менее эффективным? Если ваши объекты маленькие, и вы не клонируете их, тонна 1000х практически ничего не значит практически ничего.
      2016-12-19 20: 20: 26Z
    5. Кроме того, этот метод теряет методы (или любые вещи, которые не разрешены в JSON), плюс - JSON.stringify преобразует объекты Date в строки, ... и не наоборот;) Держись подальше от этого решения.
      2017-08-11 22: 11: 09Z

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

     
    /**
     * 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();
        };
    }
    
        
    23
    2017-12-31 12: 59: 40Z
    1. Я рекомендую удалить этот и все другие ответы, которые являются однострочными ссылками на метод
      var objToCreate = JSON.parse(JSON.stringify(cloneThis));
      
      служебной библиотеки. Они есть в каждой крупной библиотеке, а повторяющиеся краткие, не подробные ответы бесполезны для большинства посетителей, которые не будут использовать эту конкретную библиотеку.
      2016-04-07 17: 23: 49Z
    2. Более простой способ - использовать
      var objects = [{ 'a': 1 }, { 'b': 2 }];
      
      var deep = _.cloneDeep(objects);
      console.log(deep[0] === objects[0]);
      // => false
      
      . Если бы только lodash не мутировал объекты в первую очередь, тогда функция .clone(...) не была бы нужнаssary.
      2017-02-27 00: 48: 02Z
    3. Google ищет клонированные объекты JS, см. здесь. Я использую Lodash, поэтому этот ответ важен для меня. Давайте не пойдем, все "Википедия делеционист" на ответы, пожалуйста.
      2017-02-27 00: 49: 11Z
    4. В узле 9 JSON.parse (JSON.stringify (arrayOfAbout5KFlatObjects)) намного быстрее, чем _.deepClone (arrayOfAbout5KFlatObjects).
      2017-12-31 13: 07: 15Z
     _.merge({}, objA)     
    22
    2008-09-23 16: 45: 39Z
    1. Проблема с методом в том, что если у вас есть подчиненные объекты в объекте obj, будут клонированы их ссылки, а не значения каждого подобъекта.
      2009-06-25 07: 46: 29Z
    2. просто сделайте его рекурсивным, чтобы дочерние объекты были глубоко клонированы.
      2013-01-25 22: 38: 29Z
    3. просто любопытно ... не будет ли переменная-клон иметь указатели на свойства исходного объекта? потому что, кажется, нет нового выделения памяти
      2013-02-04 09: 42: 17Z
    4. Да. Это просто поверхностная копия, поэтому клон будет указывать на те же самые объекты, на которые указывает исходный объект.
      2013-02-04 10: 18: 09Z
    5. Это не ответ. Вы буквально просто наполняете объект ссылками на другой объект. Внесение изменений в исходный объект внесет изменения в «клон».
      2019-03-20 17: 34: 58Z

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

     clone

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

    EDIT:

    Это полифилл для

    function clone(obj)
     { var clone = {};
       clone.prototype = obj.prototype;
       for (property in obj) clone[property] = obj[property];
       return clone;
     }
    
    , поэтому вы также можете использовать это.  
    function object(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }
    
    var newObject = object(oldObject);
    

    ПРИМЕЧАНИЕ. Если вы используете некоторые из них, у вас могут возникнуть проблемы с некоторыми итерациями, использующими Object.create. Потому что

    var newObject = Object.create(oldObject);
    
    создает новый пустой объект, который наследует hasOwnProperty. Но это все еще полезно и практично для клонирования объектов.

    Например, если create

     oldObject

    а

     oldObject.a = 5;     
    22
    2016-03-12 14: 59: 41Z
    1. исправьте меня, если я ошибаюсь, но разве это не функция Крокфорда beget для наследования прототипа? Как это относится к клону?
      2010-10-06 15: 17: 18Z
    2. Да, я боялся этого обсуждения: какова практическая разница между клонированием, копированием и наследованием прототипа, когда следует использовать каждый из них и какие функции на этой странице на самом деле делать что? Я нашел эту страницу SO, погуглив "объект копирования javascript". То, что я действительно искал, было функцией выше, поэтому я вернулся, чтобы поделиться. Я думаю, что спрашивающий тоже искал это.
      2010-10-06 19: 51: 48Z
    3. Разница между клоном /копией и наследованием заключается в том, что - используя ваш пример, когда я изменяю свойство oldObject, свойство также изменяется в newObject. Если вы делаете копию, вы можете делать то, что вы хотите, с помощью oldObject, не меняя newObject.
      2010-12-06 13: 13: 26Z
    4. Это нарушит проверку hasOwnProperty, так что это довольно хакерский способ клонирования объекта и даст вам неожиданные результаты.
      2011-03-16 18: 17: 18Z
    5. 2014-04-24 18: 32: 03Z

    Однострочная копия с мелким копированием ( ECMAScript, 5-е издание ):

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

    Однострочное копирование мелкой копией ( 6-е издание ECMAScript , 2015): р>  var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; };     

    19
    2016-07-22 17: 27: 51Z
    1. Это может быть хорошо для простых объектов, но копирует только значения свойств. Он не касается цепочки прототипов и, используя
      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
      
      , пропускает не перечисляемые и наследуемые свойства. Кроме того, он теряет дескрипторы свойств при прямом присваивании.
      2013-11-23 17: 51: 52Z
    2. Если вы тоже скопируете прототип, вам не хватит только не перечисляемых и дескрипторов свойств, да? Довольно хорошо. :)
      2014-06-08 06: 24: 04Z
    3. Помимо производительности, это действительно удобный способ поверхностного копирования объекта. Я часто использую это для сортировки поддельных свойств покоя в назначении деструктурирования в моих компонентах React.
      2016-03-17 13: 56: 48Z

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

    var origin = { foo : {} };
    var copy = Object.assign({}, origin);
    
    console.log(origin, copy);
    console.log(origin == copy); // false
    console.log(origin.foo == copy.foo); // true
    
    также предоставляет метод глубокого копирования объектов и массивов. р>     

    17
    2016-10-15 18: 38: 31Z
    1. или его можно использовать так же, как jQiery extension: Object.keys
      2016-09-21 09: 07: 42Z
    2. @ Galvani: Следует отметить, что angular.copy и angular.extend({},obj); являются мелкими копиями. jQuery.extend - это глубокая копия.
      2016-10-15 18: 41: 00Z

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

     angular.extend     
    16
    2013-12-13 19: 00: 03Z
    1. , как другие указали в комментариях к ответу Ресига, если вы хотите клонировать массивоподобный объект, вы меняете {} на [] в вызове расширения, например jQuery.extend (true, [], obj)
      2011-03-03 21: 27: 05Z
    2. angular.copy не работает с
      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)
      
      2016-03-12 15: 08: 19Z

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

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

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

     JSON.stringify

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

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

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

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

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

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

    http://davidwalsh.name/javascript-clone

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

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

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

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

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

        
    15
    2016-07-22 17: 33: 57Z

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

     functions

    Тест циклического массива ...

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

    Функциональный тест ...

     
    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;
    }
    
        
    13
    2011-04-03 02: 08: 03Z

    AngularJS

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

     
    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
    
        
    12
    2016-09-14 13: 26: 15Z

    Я не согласен с ответом, набравшим наибольшее количество голосов здесь . Рекурсивный глубокий клон работает на намного быстрее , чем упомянутый подход JSON.parse (JSON.stringify (obj)) .

    А вот функция для быстрого ознакомления:

     
    f = new Function
    f.a = a
    ff = clone(f)
    ff === f //=> true
    ff.a === a //=> false
    
        
    11
    2017-06-18 06: 34: 37Z
    1. Мне понравился этот подход, но он не обрабатывает даты должным образом; попробуйте добавить что-то вроде
      var newObject = angular.copy(oldObject);
      
      после проверки на null `
      2017-08-21 22: 53: 25Z
    2. Сбой при циклических ссылках.
      2018-03-18 05: 19: 50Z
    3. В последнем стабильном Firefox это намного дольше, чем другие стратегии в этой ссылке Jsben.ch, на порядок или более. Это бьет других в неправильном направлении.
      2019-01-14 18: 38: 35Z
     
    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
    2018-01-17 16: 27: 11Z

    Для людей, которые хотят использовать версию if(o instanceof Date) return new Date(o.valueOf());, но не теряя объекты Date, вы можете использовать второй аргумент метода

    // 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;
    };
    
    для преобразования строк обратно в Date:

     JSON.parse(JSON.stringify(obj))     
    9
    2016-07-22 17: 47: 24Z

    Вот всеобъемлющий метод clone (), который может клонировать любой объект JavaScript. Он обрабатывает почти все случаи:

     parse     
    8
    2016-07-22 17: 31: 06Z
    1. Он преобразует примитивы в объекты-оболочки, в большинстве случаев не очень хорошее решение.
      2014-08-01 09: 58: 31Z
    2. @ DanubianSailor - я не думаю, что это так ... похоже, он возвращает примитивы сразу с самого начала и, похоже, ничего с ними не делает это превратит их в объекты-оболочки по мере их возвращения.
      2016-02-02 18: 06: 42Z
    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;
      });
    }
    
источник размещен Вот