Як зробити глибокий клон у JavaScript


105

Як ви глибоко клонуєте об’єкт Javascript?

Я знаю , що є різні функції , засновані на рамках , як JSON.parse(JSON.stringify(o))і , $.extend(true, {}, o)але я не хочу використовувати таку структуру , як це.

Який найелегантніший чи найефективніший спосіб створити глибокий клон.

Ми дбаємо про крайові випадки, такі як масив клонування. Не ламаючи прототипів ланцюгів, займаючись самовідсилами.

Нам не байдуже підтримувати копіювання DOM-об'єктів і тому подібне, тому що .cloneNodeіснує саме з цієї причини.

Оскільки я, головним чином, хочу використовувати глибокі клони при node.jsвикористанні ES5-функцій двигуна V8, це прийнятно.

[Редагувати]

Перш ніж хтось запропонує, нагадаю, існує чітка різниця між створенням копії прототипним успадкуванням від об'єкта та його клонуванням . Перший створює безлад ланцюга прототипу.

[Далі редагувати]

Прочитавши вашу відповідь, я дійшов до прикрого відкриття, що клонування цілих предметів - це дуже небезпечна і складна гра. Візьмемо для прикладу наступний об’єкт на основі закриття

var o = (function() {
     var magic = 42;

     var magicContainer = function() {
          this.get = function() { return magic; };
          this.set = function(i) { magic = i; };
     }

      return new magicContainer;
}());

var n = clone(o); // how to implement clone to support closures

Чи є спосіб записати функцію клонування, яка клонує об'єкт, має той самий стан під час клонування, але не може змінити стан oбез запису JS-аналізатора в JS.

Більше не повинно бути реальної потреби в такій функції. Це лише академічний інтерес.


2
Перш ніж він став позначеним дублікатом, я переглянув stackoverflow.com/questions/122102/… і не знайшов жодної відповіді, яка стосувалась усіх крайових справ.
Райнос

Вимоги в розділі «подальше редагування» неможливо досягти без «допомоги» самого об’єкта, оскільки такі приватні змінні є справді приватними і, як наслідок, недоступними загальною cloneфункцією. Розглянутий об’єкт повинен викрити власний індивідуальний cloneметод.
trincot

Я читав про це купу сьогодні ввечері, і серед знайдених нами
Кіт

Відповіді:


67

Це дійсно залежить від того, що ви хотіли б клонувати. Це справді об'єкт JSON чи просто будь-який об’єкт у JavaScript? Якщо ви хочете зробити будь-який клон, це може зіткнутися з вами. Яка біда? Я поясню це нижче, але спочатку кодовий приклад, який клонує об’єктні літерали, будь-які примітиви, масиви та вузли DOM.

function clone(item) {
    if (!item) { return item; } // null, undefined values check

    var types = [ Number, String, Boolean ], 
        result;

    // normalizing primitives if someone did new String('aaa'), or new Number('444');
    types.forEach(function(type) {
        if (item instanceof type) {
            result = type( item );
        }
    });

    if (typeof result == "undefined") {
        if (Object.prototype.toString.call( item ) === "[object Array]") {
            result = [];
            item.forEach(function(child, index, array) { 
                result[index] = clone( child );
            });
        } else if (typeof item == "object") {
            // testing that this is DOM
            if (item.nodeType && typeof item.cloneNode == "function") {
                result = item.cloneNode( true );    
            } else if (!item.prototype) { // check that this is a literal
                if (item instanceof Date) {
                    result = new Date(item);
                } else {
                    // it is an object literal
                    result = {};
                    for (var i in item) {
                        result[i] = clone( item[i] );
                    }
                }
            } else {
                // depending what you would like here,
                // just keep the reference, or create new object
                if (false && item.constructor) {
                    // would not advice to do that, reason? Read below
                    result = new item.constructor();
                } else {
                    result = item;
                }
            }
        } else {
            result = item;
        }
    }

    return result;
}

var copy = clone({
    one : {
        'one-one' : new String("hello"),
        'one-two' : [
            "one", "two", true, "four"
        ]
    },
    two : document.createElement("div"),
    three : [
        {
            name : "three-one",
            number : new Number("100"),
            obj : new function() {
                this.name = "Object test";
            }   
        }
    ]
})

А тепер поговоримо про проблеми, які можуть виникнути під час клонування об'єктів REAL. Я зараз говорю про об’єкти, які ви створюєте, роблячи щось подібне

var User = function(){}
var newuser = new User();

Звичайно, ви можете їх клонувати, це не проблема, кожен об’єкт виявляє властивість конструктора, і ви можете використовувати його для клонування об'єктів, але це не завжди спрацює. Ви також можете робити просто for inна цих об'єктах, але це йде в тому ж напрямку - неприємності. Я також включив функціональність клонування всередині коду, але це виключено за допомогою if( false )заяви.

Отже, чому клонування може бути болем? Ну, по-перше, кожен об'єкт / екземпляр може мати певний стан. Ви ніколи не можете бути впевнені, що у ваших об'єктів немає, наприклад, приватних змінних, і якщо це так, клонуючи об’єкт, ви просто порушите стан.

Уявіть, що немає держави, це добре. Тоді у нас ще є інша проблема. Клонування методом "конструктор" дасть нам ще одну перешкоду. Це залежність від аргументів. Ви ніколи не можете бути впевнені, що хтось, хто створив цей об’єкт, цього не зробив, якийсь

new User({
   bike : someBikeInstance
});

Якщо це так, вам не пощастило, деякийBikeInstance був, ймовірно, створений в якомусь контексті, і цей контекст невідомий для методу клонування.

То що робити? Ти ще можеш зробитиfor in рішення та обробляти такі об'єкти, як звичайні літеральні об'єкти, але, можливо, це ідея взагалі не клонувати таких об'єктів, а просто передати посилання на цей об’єкт?

Інше рішення - ви можете встановити умову, що всі об'єкти, які повинні бути клоновані, повинні реалізовувати цю частину самостійно та надавати відповідний метод API (наприклад, cloneObject). Щось, що cloneNodeробить для DOM.

Тобі вирішувати.


Я підійшов до перешкод, що стосуються предметів, які використовують закриття для приховування стану. Як можна клонувати об’єкт і весь його стан, але все-таки гарантувати, що клон не може сам змінити початковий стан. Куєнтер вказує на result = new item.constructor();те, що це погано, це те, що, враховуючи функцію конструктора та об'єкт елемента, ви маєте змогу переглядати будь-які параметри, передані в конструктор.
Райнос

7
@Raynos: якщо об’єкти використовують закриття для приховування стану, ви не можете їх клонувати. Звідси термін «закриття». Як говорить в кінці кінця nemisj, найкращим способом є реалізація методу API для клонування (або серіалізації / десеріалізації), якщо це є можливим.
Міхель Калкман

@MichielKalkman У мене було відчуття, що так було. Хоча хтось, можливо, мав справді розумне рішення цього питання.
Райнос

Який ефект false && item.constructor? Хіба це не ifмарно?
Габріель Петрова

2
@GabrielPetrovay Це if"марно" з функціональної точки зору, тому що він ніколи не буде працювати, але він має академічну мету показати гіпотетичну реалізацію, яку можна спробувати використовувати, яку автор не радить через причину, пояснену пізніше. Так, так, він буде запускати elseзауваження щоразу, коли умова оцінюється, і все ж є причина в наявності коду.
Gui Imamura

155

Дуже простий спосіб, можливо занадто простий:

var cloned = JSON.parse(JSON.stringify(objectToClone));

12
Чудово, якщо значення об'єкта не є функцією, і тоді вам доведеться використовувати щось на зразок прийнятої відповіді. Або скористайтеся функцією помічника, як cloneDeepу Лодаша.
matthoiland

31
Якщо значення об'єкта є функцією, об'єктом є не JSON.
Джос де Йонг

5
Який випадок використання може виправдати клонування функції, а не просто її використання?
Г. Гез

3
Якщо я правильно пам'ятаю, це також перетворює дати в рядки.
Пітер

3
@ G.Ghez, якщо ви клонуєте об’єкт, який містить функцію, ця функція буде втрачена ..
Петро

38

JSON.parse(JSON.stringify())Поєднання глибокого копіювання об'єктів Javascript є неефективним хак, як це було призначене для даних JSON. Він не підтримує значення undefinedабо function () {}, і просто буде ігнорувати їх (або nullїх) при "розшифровці" (маршируванні) об'єкта Javascript в JSON.

Кращим рішенням є використання функції глибокого копіювання. Функція внизу копіює об'єкти, і не потребує сторонньої бібліотеки (jQuery, LoDash тощо).

function copy(aObject) {
  if (!aObject) {
    return aObject;
  }

  let v;
  let bObject = Array.isArray(aObject) ? [] : {};
  for (const k in aObject) {
    v = aObject[k];
    bObject[k] = (typeof v === "object") ? copy(v) : v;
  }

  return bObject;
}

7
За винятком випадків, коли aObject (або інший об'єкт, який він містить) містить само посилання на себе ... stackoverflow ™!
e2-e4

@ ringø - Чи можете ви надати кілька тестових випадків "самонавіювання"?
tfmontague

4
var o = { a:1, b:2 } ; o["oo"] = { c:3, m:o };
e2-e4

4
Мені подобається це рішення. Єдине виправлення для мене стосувалося нульових значень: bObject[k] = (v === null) ? null : (typeof v === "object") ? copy(v) : v;
David Kirkland

2
Ця функція проста, зрозуміла і охопить майже всі випадки. У світі JavaScript це настільки ж ідеально, як ви можете отримати.
icc97

21

Ось функція ES6, яка також буде працювати для об'єктів з циклічними посиланнями:

function deepClone(obj, hash = new WeakMap()) {
    if (Object(obj) !== obj) return obj; // primitives
    if (hash.has(obj)) return hash.get(obj); // cyclic reference
    const result = obj instanceof Set ? new Set(obj) // See note about this!
                 : obj instanceof Map ? new Map(Array.from(obj, ([key, val]) => 
                                        [key, deepClone(val, hash)])) 
                 : obj instanceof Date ? new Date(obj)
                 : obj instanceof RegExp ? new RegExp(obj.source, obj.flags)
                 // ... add here any specific treatment for other classes ...
                 // and finally a catch-all:
                 : obj.constructor ? new obj.constructor() 
                 : Object.create(null);
    hash.set(obj, result);
    return Object.assign(result, ...Object.keys(obj).map(
        key => ({ [key]: deepClone(obj[key], hash) }) ));
}

// Sample data
var p = {
  data: 1,
  children: [{
    data: 2,
    parent: null
  }]
};
p.children[0].parent = p;

var q = deepClone(p);

console.log(q.children[0].parent.data); // 1

Примітка про набори та карти

Як поводитися з ключами наборів і карт є дискусійним: ці клавіші часто примітиви (у цьому випадку дебатів немає), але вони також можуть бути об'єктами. У такому випадку виникає питання: чи слід клавішувати ці ключі?

Можна стверджувати, що це слід робити, так що якщо ці об'єкти мутуються в копії, об'єкти в оригіналі не зачіпаються, і навпаки.

З іншого боку, хотілося б, щоби встановити / мапу has ключ встановити, це має бути правдою як в оригіналі, так і в копії - принаймні до того, як будь-яка з них буде внесена будь-яка зміна. Було б дивно, якби копія була набором / картою, в якій є ключі, які ніколи раніше не виникали (як вони були створені під час процесу клонування): безумовно, це не дуже корисно для будь-якого коду, який повинен знати, чи є даний об'єкт введіть цей набір / карта чи ні.

Як ви помічаєте, я більше друга думка: ключі Sets and Maps - це значення (можливо, посилання ), які повинні залишатися однаковими.

Такі варіанти часто також виникають з іншими (можливо, на замовлення) об'єктами. Загального рішення немає, оскільки багато залежить від того, як очікується, що поводиться клонований об’єкт у вашому конкретному випадку.


не обробляє дату та regexp
mkeremguc

1
@mkeremguc, дякую за ваш коментар Я оновив код, щоб підтримати дату та regexp.
трінкот

Приємне рішення, включаючи карту. Щойно відсутня підтримка для клонування наборів es6.
Роберт Біггс

Якщо я не помиляюся, ви, напевно, можете додати підтримку наборів за допомогою:if (object instanceof Set) Array.from(object, val => result.add(deepClone(val, hash)));
Роберт Біггс

@RobertBiggs, це одна з можливостей, але, на мою думку, якщо набір має певний ключ, це також має бути правдою у клонованій версії цього набору. З запропонованим кодом це не відповідає дійсності, якщо ключі є об'єктами. Тому я б запропонував не клонувати ключі - я дійсно думаю, що тоді він буде вести себе так, як очікувалося. Дивіться оновлення моєї відповіді з цього приводу.
trincot

10

Бібліотека дописів Underscore.js має функцію, яку називають знімком який глибоко клонує об’єкт

фрагмент від джерела:

snapshot: function(obj) {
  if(obj == null || typeof(obj) != 'object') {
    return obj;
  }

  var temp = new obj.constructor();

  for(var key in obj) {
    if (obj.hasOwnProperty(key)) {
      temp[key] = _.snapshot(obj[key]);
    }
  }

  return temp;
}

як тільки бібліотека буде пов'язана з вашим проектом, викликайте функцію просто за допомогою

_.snapshot(object);

4
хороше рішення, лише момент, який слід пам’ятати: клон та оригінал поділяють той самий прототип. якщо це проблема, можливо додати "temp .__ proto__ = .snapshot (obj .__ proto_ );" праворуч над "темпом повернення", а для підтримки вбудованих класів із властивостями, позначеними як "без перерахування", ви можете перебирати на getOwnPropertyNames () замість "для (var ключ в obj)"
Ronen Ness

3

Це метод глибокого клонування, який я використовую, я вважаю це чудовим, сподіваюсь, ви робите пропозиції

function deepClone (obj) {
    var _out = new obj.constructor;

    var getType = function (n) {
        return Object.prototype.toString.call(n).slice(8, -1);
    }

    for (var _key in obj) {
        if (obj.hasOwnProperty(_key)) {
            _out[_key] = getType(obj[_key]) === 'Object' || getType(obj[_key]) === 'Array' ? deepClone(obj[_key]) : obj[_key];
        }
    }
    return _out;
}

3

Як зауважували інші щодо цього та подібних питань, клонування «об’єкта», загалом, в JavaScript є сумнівним.

Однак існує клас об'єктів, який я називаю об'єктами "даних", тобто ті, що побудовані просто з { ... }літералів та / або простих присвоєнь властивостей або десеріалізовані з JSON, для яких розумно хотіти клонувати. Просто сьогодні я хотів штучно надути дані, отримані від сервера 5 разів, щоб перевірити, що відбувається з великим набором даних, але об'єкт (масив) та його діти повинні були бути окремими об'єктами, щоб речі нормально функціонували. Клонування дозволило мені зробити це для множення мого набору даних:

return dta.concat(clone(dta),clone(dta),clone(dta),clone(dta));

Інше місце, в якому я часто закінчую клонування об'єктів даних, - це повернення даних до хоста, де я хочу позбавити поля стану від об'єкта в моделі даних перед надсиланням. Наприклад, я можу захотіти зняти всі об'єкти, починаючи з "_" від об'єкта під час його клонування.

Це код, який я закінчив писати, щоб це зробити загально, включаючи підтримуючі масиви та селектор, щоб вибрати членів, які потрібно клонувати (який використовує рядок "шлях" для визначення контексту):

function clone(obj,sel) {
    return (obj ? _clone("",obj,sel) : obj);
    }

function _clone(pth,src,sel) {
    var ret=(src instanceof Array ? [] : {});

    for(var key in src) {
        if(!src.hasOwnProperty(key)) { continue; }

        var val=src[key], sub;

        if(sel) {
            sub+=pth+"/"+key;
            if(!sel(sub,key,val)) { continue; }
            }

        if(val && typeof(val)=='object') {
            if     (val instanceof Boolean) { val=Boolean(val);        }
            else if(val instanceof Number ) { val=Number (val);        }
            else if(val instanceof String ) { val=String (val);        }
            else                            { val=_clone(sub,val,sel); }
            }
        ret[key]=val;
        }
    return ret;
    }

Найпростішим розумним глибоким клонуванням рішення, припускаючи ненульовий кореневий об'єкт і без вибору члена, є:

function clone(src) {
    var ret=(src instanceof Array ? [] : {});
    for(var key in src) {
        if(!src.hasOwnProperty(key)) { continue; }
        var val=src[key];
        if(val && typeof(val)=='object') { val=clone(val);  }
        ret[key]=val;
        }
    return ret;
    }

2

Lo-Dash , який тепер є набором Underscore.js , має кілька функцій глибокого клонування:

З відповіді самого автора :

lodash underscore побудова надається для забезпечення сумісності з останньою стабільною версією підкреслення.


Питання сказало: "Я не хочу користуватися бібліотеками"
Femi Oni

@FemiOni питання не має нічого про бібліотеки (навіть у старих редакціях) ... Декілька інших відповідей тут також використовують одну чи іншу бібліотеку.
CPHPython

@FemiOni відповідь оскаржена вчора. Цікаво ... У будь-якому випадку, це місце для навчання і на всякий випадок, якщо хтось насправді сам реалізує глибокий клон, вихідний код lodashbaseClone може дати деякі ідеї.
CPHPython

@FemiOni об’єкт JSON не є ні бібліотекою, ні рамкою ... Якщо ви збираєтесь реалізувати цю функцію, пропоную заглянути в одну з відкритих джерел і використовувати потрібні вам частини (багато хто перевірявся роками). Це дозволить уникнути помилок і пропущених міркувань у довгостроковій перспективі.
CPHPython

2

Наведена нижче функція є найбільш ефективним способом глибокого клонування об’єктів JavaScript.

function deepCopy(obj){
    if (!obj || typeof obj !== "object") return obj;

    var retObj = {};

    for (var attr in obj){
        var type = obj[attr];

        switch(true){
            case (type instanceof Date):
                var _d = new Date();
                _d.setDate(type.getDate())
                retObj[attr]= _d;
                break;

            case (type instanceof Function):
                retObj[attr]= obj[attr];
                break;

            case (type instanceof Array):
                var _a =[];
                for (var e of type){
                    //_a.push(e);
                    _a.push(deepCopy(e));
                }
                retObj[attr]= _a;
                break;

            case (type instanceof Object):
                var _o ={};
                for (var e in type){
                    //_o[e] = type[e];
                    _o[e] = deepCopy(type[e]);
                }
                retObj[attr]= _o;
                break;

            default:
                retObj[attr]= obj[attr];
        }
    }
    return retObj;
}

var obj = {
    string: 'test',
    array: ['1'],
    date: new Date(),
    object:{c: 2, d:{e: 3}},
    function: function(){
        return this.date;
    }
};

var copyObj = deepCopy(obj);

console.log('object comparison', copyObj === obj); //false
console.log('string check', copyObj.string === obj.string); //true
console.log('array check', copyObj.array === obj.array); //false
console.log('date check', copyObj2.date === obj.date); //false
console.log('object check', copyObj.object === obj.object); //false
console.log('function check', copyObj.function() === obj.function()); //true

2
Чи є у вас якісь аргументи на підтвердження вашої претензії?
Fabian von Ellerts

Я наводив приклад нижче функції. У вас є сумніви?
Kooldandy

Ці приклади показують, що функція працює, що класно, але чому це "найефективніший спосіб"?
Fabian von Ellerts

Тому що це рекурсивно копіює атрибути об’єкта в єдиний цикл. Також дата, функція, об’єкт, масив, номер, рядки всі тиси обробляються належним чином. Чи є у вас якийсь інший шлях?
Kooldandy

2

Більше не повинно бути реальної потреби в такій функції. Це лише академічний інтерес.

Як суто вправа, це більш функціональний спосіб робити це. Це розширення відповіді @ tfmontague, як я запропонував би там додати блок охорони. Але бачу, як я відчуваю себе змушеним до ES6 і функціоналізую всі речі, ось моя сутенерна версія. Це ускладнює логіку, оскільки вам доведеться відображати масив і зменшувати над об'єктом, але це дозволяє уникнути будь-яких мутацій.

const cloner = (x) => {
    const recurseObj = x => (typeof x === 'object') ? cloner(x) : x
    const cloneObj = (y, k) => {
        y[k] = recurseObj(x[k])
        return y
    }
    // Guard blocks
    // Add extra for Date / RegExp if you want
    if (!x) {
        return x
    }
    if (Array.isArray(x)) {
        return x.map(recurseObj)
    }
    return Object.keys(x).reduce(cloneObj, {})
}
const tests = [
    null,
    [],
    {},
    [1,2,3],
    [1,2,3, null],
    [1,2,3, null, {}],
    [new Date('2001-01-01')], // FAIL doesn't work with Date
    {x:'', y: {yx: 'zz', yy: null}, z: [1,2,3,null]},
    {
        obj : new function() {
            this.name = "Object test";
        }
    } // FAIL doesn't handle functions
]
tests.map((x,i) => console.log(i, cloner(x)))


1

Я помітив, що Карта повинна вимагати спеціального звернення, тому з усіма пропозиціями в цій темі код буде:

function deepClone( obj ) {
    if( !obj || true == obj ) //this also handles boolean as true and false
        return obj;
    var objType = typeof( obj );
    if( "number" == objType || "string" == objType ) // add your immutables here
        return obj;
    var result = Array.isArray( obj ) ? [] : !obj.constructor ? {} : new obj.constructor();
    if( obj instanceof Map )
        for( var key of obj.keys() )
            result.set( key, deepClone( obj.get( key ) ) );
    for( var key in obj )
        if( obj.hasOwnProperty( key ) )
            result[key] = deepClone( obj[ key ] );
    return result;
}

1

моє доповнення до всіх відповідей

deepCopy = arr => {
  if (typeof arr !== 'object') return arr
  if(arr.pop) return [...arr].map(deepCopy)
  const copy = {}
  for (let prop in arr)
    copy[prop] = deepCopy(arr[prop])
  return copy
}

0

Це працює для масивів, об'єктів та примітивів. Подвійно рекурсивний алгоритм, який перемикається між двома методами обходу:

const deepClone = (objOrArray) => {

  const copyArray = (arr) => {
    let arrayResult = [];
    arr.forEach(el => {
        arrayResult.push(cloneObjOrArray(el));
    });
    return arrayResult;
  }

  const copyObj = (obj) => {
    let objResult = {};
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        objResult[key] = cloneObjOrArray(obj[key]);
      }
    }
    return objResult;
  }

  const cloneObjOrArray = (el) => {
    if (Array.isArray(el)) {
      return copyArray(el);
    } else if (typeof el === 'object') {
      return copyObj(el);
    } else {
      return el;
    }
  }

  return cloneObjOrArray(objOrArray);
}

0

Ми можемо використовувати рекурсію для виготовлення deepCopy. Він може створювати копію масиву, об'єкта, масиву об'єкта, об'єкта з функцією. якщо ви хочете, ви можете додати функцію для іншого типу структури даних, наприклад, карта тощо.

function deepClone(obj) {
         var retObj;
        _assignProps = function(obj, keyIndex, retObj) {
               var subType = Object.prototype.toString.call(obj[keyIndex]);
               if(subType === "[object Object]" || subType === "[object Array]") {
                    retObj[keyIndex] = deepClone(obj[keyIndex]);
               }
               else {
                     retObj[keyIndex] = obj[keyIndex];
               }
        };

        if(Object.prototype.toString.call(obj) === "[object Object]") {
           retObj = {};
           for(key in obj) {
               this._assignProps(obj, key, retObj);
           }
        }
        else if(Object.prototype.toString.call(obj) == "[object Array]") {
           retObj = [];
           for(var i = 0; i< obj.length; i++) {
              this._assignProps(obj, i, retObj);
            }
        };

        return retObj;
    };

0

Використовуйте незмінні JS

import { fromJS } from 'immutable';

// An object we want to clone
let objA = { 
   a: { deep: 'value1', moreDeep: {key: 'value2'} } 
};

let immB = fromJS(objA); // Create immutable Map
let objB = immB.toJS(); // Convert to plain JS object

console.log(objA); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }
console.log(objB); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }

// objA and objB are equalent, but now they and their inner objects are undependent
console.log(objA === objB); // false
console.log(objA.a === objB.a); // false
console.log(objA.moreDeep === objB.moreDeep); // false

Або лодаш / злиття

import merge from 'lodash/merge'

var objA = {
    a: [{ 'b': 2 }, { 'd': 4 }]
};
// New deeply cloned object:
merge({}, objA ); 

// We can also create new object from several objects by deep merge:
var objB = {
    a: [{ 'c': 3 }, { 'e': 5 }]
};
merge({}, objA , objB ); // Object { a: [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }

0

Цей за допомогою кругової довідки працює для мене

 //a test-object with circular reference :
 var n1 = {   id:0,   text:"aaaaa",   parent:undefined} 
 var n2 = {  id:1,   text:"zzzzz",   parent:undefined } 
 var o = { arr:[n1,n2],   parent:undefined } 
 n1.parent = n2.parent = o;
 var obj = {   a:1,   b:2,   o:o }
 o.parent = obj;

 function deepClone(o,output){ 

     if(!output) output = {};  
     if(o.______clone) return o.______clone;
     o.______clone = output.______clone = output;

   for(var z in o){

     var obj = o[z];
     if(typeof(obj) == "object") output[z] = deepClone(obj)
     else output[z] = obj; 
    }

   return output;
}

console.log(deepClone(obj));

0

var newDate = нова дата (this.oldDate); Я передавав oldDate для функціонування та генерування newDate з цього .oldDate, але він міняв і це.


0

Це рішення дозволить уникнути рекурсійних проблем при використанні [... target] або {... target}

function shallowClone(target) {
  if (typeof a == 'array') return [...target]
  if (typeof a == 'object') return {...target}
  return target
}

/* set skipRecursion to avoid throwing an exception on recursive references */
/* no need to specify refs, or path -- they are used interally */
function deepClone(target, skipRecursion, refs, path) {
  if (!refs) refs = []
  if (!path) path = ''
  if (refs.indexOf(target) > -1) {
    if (skipRecursion) return null
    throw('Recursive reference at ' + path)
  }
  refs.push(target)
  let clone = shallowCopy(target)
  for (i in target) target[i] = deepClone(target, refs, path + '.' + i)
  return clone
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.