Кой е най-ефективният начин за дълбоко клониране на обект в JavaScript?

Кой е най-ефективният начин за клониране на обект в JavaScript? Виждал съм, че се използва obj = eval(uneval(o));, но това е нестандартно и се поддържа само от Firefox.

Правил съм неща като obj = JSON.parse(JSON.stringify(o));, но се съмнявам в ефективността.

Виждал съм и рекурсивни функции за копиране с различни недостатъци.
Изненадан съм, че не съществува канонично решение.

Решение

Родно дълбоко клониране

Нарича се "структурирано клониране", работи експериментално в Node 11 и по-нови версии и се надяваме, че ще се появи в браузърите. Вижте този отговор за повече подробности.

Бързо клониране със загуба на данни - JSON.parse/stringify

Ако не използвате Dates, функции, undefined, Infinity, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays или други сложни типове във вашия обект, много прост one liner за дълбоко клониране на обект е:

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's за еталони.

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

Тъй като клонирането на обекти не е тривиално (сложни типове, кръгови препратки, функции и т.н.), повечето големи библиотеки предоставят функция за клониране на обекти. Не изобретявайте колелото - ако вече използвате някоя библиотека, проверете дали тя има функция за клониране на обекти. Например,

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

ES6

За пълнота отбележете, че ES6 предлага два механизма за повърхностно копиране: Object.assign() и spread operator.

Коментари (24)

Ако няма вграден такъв, можете да опитате:

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;
}
Коментари (11)
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }
Коментари (5)