JavaScript'te derin klonlama


105

Bir Javascript nesnesini nasıl derinlemesine klonlarsınız?

Ben gibi çerçeveler dayalı çeşitli fonksiyonlar vardır biliyor JSON.parse(JSON.stringify(o))ve $.extend(true, {}, o)ama böyle bir çerçeve kullanmak istemiyoruz.

Derin bir klon oluşturmanın en zarif veya etkili yolu nedir?

Dizileri klonlama gibi uç durumları önemsiyoruz. Prototip zincirlerini kırmamak, öz referansla uğraşmak.

DOM nesnelerinin kopyalanmasını desteklemeyi umursamıyoruz ve .cloneNodebu nedenle var olduğu için gibi .

node.jsV8 motorunun ES5 özelliklerini kullanırken esas olarak derin klon kullanmak istediğim için kabul edilebilir.

[Düzenle]

Herhangi biri önermeden önce, nesneden prototip olarak miras alarak bir kopya oluşturmakla onu klonlamak arasında belirgin bir fark olduğunu belirtmeme izin verin . İlki, prototip zincirini karıştırıyor.

[Daha Fazla Düzenleme]

Cevabınızı okuduktan sonra, tüm nesneleri klonlamanın çok tehlikeli ve zor bir oyun olduğuna dair can sıkıcı keşfe geldim. Örneğin aşağıdaki kapanış tabanlı nesneyi alın

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

Nesneyi klonlayan, klonlama sırasında aynı duruma sahip olan ancak oJS'de bir JS ayrıştırıcısı yazmadan durumunu değiştiremeyen bir klon işlevi yazmanın herhangi bir yolu var mı ?

Artık böyle bir işleve gerçek dünyada ihtiyaç duyulmamalıdır. Bu sadece akademik bir ilgi.


2
Yinelenen olarak işaretlenmeden önce, stackoverflow.com/questions/122102/… adresine baktım ve tüm uç durumlarla ilgili herhangi bir yanıt bulamadım.
Raynos

"Daha fazla düzenleme" bölümündeki gereksinimin, nesnenin kendisinin "yardımı" olmadan gerçekleştirilmesi imkansızdır, çünkü bu tür özel değişkenler gerçekten özeldir ve bu nedenle genel bir cloneişlev tarafından erişilebilir değildir . Söz konusu nesne kendi özel cloneyöntemini ortaya koymalıdır .
trincot

Bu gece bunu bir sürü okudum ve bulduğum kaynaklar arasında, tarayıcıdaki yapılandırılmış klon algoritmasına erişmek için birkaç hack içeren bu çirkin görünümlü blog yazısı vardı: dassur.ma/things/deep-copy
Cat

Yanıtlar:


67

Gerçekten ne klonlamak istediğinize bağlı. Bu gerçekten bir JSON nesnesi mi yoksa JavaScript'teki herhangi bir nesne mi? Herhangi bir klon yapmak isterseniz, başınızı belaya sokabilir. Hangi sorun? Aşağıda açıklayacağım, ancak önce nesne değişmezlerini, ilkelleri, dizileri ve DOM düğümlerini klonlayan bir kod örneği.

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

Şimdi de GERÇEK nesneleri klonlamaya başladığınızda karşılaşabileceğiniz problemlerden bahsedelim. Şimdi konuşuyorum, benzer bir şey yaparak yarattığınız nesnelerden bahsediyorum

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

Elbette onları klonlayabilirsiniz, bu bir problem değildir, her nesne yapıcı özelliğini açığa çıkarır ve nesneleri klonlamak için kullanabilirsiniz, ancak bu her zaman çalışmayacaktır. for inBu nesneler üzerinde basit de yapabilirsiniz , ancak aynı yöne gider - sorun. Kodun içine klonlama işlevini de ekledim, ancak if( false )ifade tarafından hariç tutuldu .

Öyleyse klonlama neden bir acı olabilir? Her şeyden önce, her nesnenin / örneğin bir durumu olabilir. Nesnelerinizin, örneğin özel değişkenlere sahip olmadığından asla emin olamazsınız ve bu durumda, nesneyi klonlayarak, durumu kırarsınız.

Devletin olmadığını hayal edin, sorun değil. O halde hala başka bir sorunumuz var. "Oluşturucu" yöntemiyle klonlamak bize başka bir engel oluşturacaktır. Bu bir argüman bağımlılığıdır. Bu nesneyi yaratan birinin yapmadığından asla emin olamazsınız, bir tür

new User({
   bike : someBikeInstance
});

Durum buysa, şansınız kalmaz, someBikeInstance muhtemelen bir bağlamda oluşturulmuştur ve bu bağlam klon yöntemi için bilinmemektedir.

Peki ne yapmalı? Hala for inçözüm yapabilir ve bu tür nesnelere normal nesne değişmezleri gibi davranabilirsiniz, ancak bu tür nesneleri hiç klonlamamak ve sadece bu nesnenin referansını geçmek bir fikir olabilir mi?

Başka bir çözüm de - klonlanması gereken tüm nesnelerin bu bölümü kendi başlarına uygulaması ve uygun API yöntemini (cloneObject gibi) sunması için bir kural belirleyebilirsiniz. cloneNodeDOM için yapılan bir şey .

Sen karar ver.


Durumu kendim gizlemek için kapak kullanan nesnelerle uğraşma engelini aştım. Bir nesneyi ve tüm durumunu nasıl klonlayabilir, ancak yine de klonun orijinal durumunu kendi başına değiştiremeyeceğinden emin olabilirsiniz. result = new item.constructor();Kötü olmanın bir couinter noktası , yapıcı işlevi ve öğe nesnesi göz önüne alındığında, yapıcıya geçirilen herhangi bir parametreyi RE yapabilmeniz gerektiğidir.
Raynos

7
@Raynos: Eğer nesneler durumu gizlemek için kapatmalar kullanırsa, onları klonlayamazsınız. Dolayısıyla 'kapanış' terimi. Nemisj'in sonunda dediği gibi, en iyi yol, eğer bir seçenekse, klonlama (veya serileştirme / serileştirme) için bir API yöntemi uygulamaktır.
Michiel Kalkman

@MichielKalkman Durumun böyle olduğunu hissettim. Yine de birisi buna gerçekten akıllıca bir çözüm bulmuş olabilir.
Raynos

Etkisi nedir false && item.constructor? Bu ifişe yaramaz değil mi?
Gabriel Petrovay

2
@GabrielPetrovay Bu ifişlevsel açıdan 'yararsız' çünkü asla çalışmayacak, ancak akademik bir amacı var, birinin kullanmaya çalışabileceği varsayımsal bir uygulamayı gösterme gibi, yazar daha sonra açıklanacak sebepten dolayı tavsiye vermiyor. Yani evet, elsekoşul her değerlendirildiğinde cümleciği tetikleyecektir ve yine de kodun orada olmasının bir nedeni vardır.
Gui Imamura

156

Çok basit bir yol, belki çok basit:

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

12
Bir nesne değeri bir işlev olmadığı sürece harika, bu noktada kabul edilen yanıt gibi bir şey kullanmanız gerekecek. Veya cloneDeepLodash'taki gibi bir yardımcı işlev kullanın .
matthoiland

31
Bir nesne değeri bir işlevse, nesne JSON değildir.
Jos de Jong

6
Hangi kullanım durumu bir işlevi kullanmak yerine klonlamayı haklı gösterebilir?
G. Ghez

3
Doğru hatırlıyorsam, bu da tarihleri ​​dizelere dönüştürür.
Peter

3
@ G.Ghez, işlevi kaybolacak bir işlev içeren bir nesneyi klonlarsanız ..
Peter

38

JSON.parse(JSON.stringify())O JSON veri için gerektiği gibi JavaScript nesneleri derin kopyasına kombinasyonu etkisiz kesmek olduğunu. undefinedVeya değerlerini desteklemez ve Javascript nesnesini JSON'a "dizdirirken" (sıralarken) function () {}bunları (veya nullonları) yok sayar.

Daha iyi bir çözüm, derin bir kopyalama işlevi kullanmaktır. Aşağıdaki işlev nesneleri derinlemesine kopyalar ve üçüncü taraf bir kitaplık (jQuery, LoDash, vb.) Gerektirmez.

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
Bir Nesnenin (veya içerdiği başka bir nesnenin) kendisine bir öz referans içermesi dışında ... stackoverflow ™!
e2-e4

@ ringø - Bazı "kendi kendine referans" test durumları sağlayabilir misiniz?
tfmontague

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

4
Bu çözümü beğendim. Benim için tek düzeltme boş değerlerle uğraşmaktı: bObject[k] = (v === null) ? null : (typeof v === "object") ? copy(v) : v;
David Kirkland

2
Bu işlev basit, anlaşılır ve neredeyse tüm durumları yakalayacaktır. JavaScript dünyasında, alabileceğiniz kadar mükemmel.
icc97

22

Döngüsel referanslara sahip nesneler için de çalışacak bir ES6 işlevi:

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

Setler ve Haritalar hakkında bir not

Nasıl Setleri ve Haritalarının tuşları ile uğraşmak kuşkusuzdur: bu anahtarlar genellikle ilkel (bu durumda yanlışım yok), ancak bunlar olabilir de nesneler olabilir. Bu durumda soru şu hale gelir: bu anahtarlar klonlanmalı mı?

Bunun yapılması gerektiği iddia edilebilir, böylece bu nesneler kopyada değiştirilirse, orijinaldeki nesneler etkilenmez ve bunun tersi de geçerlidir.

Öte yandan, eğer bir Set / Map hasbir anahtar ise, bunun hem orijinalde hem de kopyada - en azından ikisinde herhangi bir değişiklik yapılmadan önce - doğru olması istenecektir. Kopyanın daha önce hiç gerçekleşmemiş anahtarlara sahip bir Set / Map olması garip olurdu (klonlama işlemi sırasında yaratıldıkları gibi): kesinlikle bu, belirli bir nesnenin bir nesne olup olmadığını bilmesi gereken herhangi bir kod için pek kullanışlı değildir. Bu Set / Map'i girin veya girin.

Fark ettiğiniz gibi, ben daha çok ikinci görüşüm: Setlerin ve Haritaların anahtarları aynı kalması gereken değerlerdir (belki referanslar ).

Bu tür seçimler genellikle diğer (belki özel) nesnelerle de yüzeye çıkar. Genel bir çözüm yoktur, çünkü klonlanan nesnenin sizin özel durumunuzda nasıl davranması beklendiğine bağlıdır.


tarih ve regexp işlemez
mkeremguc

1
@mkeremguc, yorumunuz için teşekkürler. Tarihi ve regexp'yi desteklemek için kodu güncelledim.
trincot

Harita dahil güzel çözüm. Sadece es6 setlerini klonlamak için eksik destek.
Robert Biggs

Yanılmıyorsam, Setler için muhtemelen şu şekilde destek ekleyebilirsiniz:if (object instanceof Set) Array.from(object, val => result.add(deepClone(val, hash)));
Robert Biggs

@RobertBiggs, bu bir olasılık, ancak bence bir Setin belirli bir anahtarı varsa, bu Setin klonlanmış versiyonunda da doğru olmalı. Önerilen kodunuzla, anahtarlar nesne ise bu doğru olmaz. Bu nedenle ben öneririm değil anahtarlarını klonlama - Gerçekten beklendiği gibi daha sonra davranmaya düşünüyorum. Bu bakımdan cevabımın güncellemesine bakın.
trincot

10

Underscore.js contrib kütüphane kütüphane adında bir işlevi vardır anlık derin klonların bir nesne olduğunu

kaynaktan snippet:

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

Kütüphane projenize bağlandığında, işlevi basitçe kullanarak çağırın

_.snapshot(object);

4
iyi bir çözüm, hatırlanması gereken bir nokta: klon ve orijinal aynı prototipi paylaşıyor. bu bir problemse, "temp .__ proto__ = .snapshot (obj .__ proto_ )" eklemek mümkündür ; "dönüş geçici" nin hemen üstünde ve 'numaralandırılmamış' olarak işaretlenmiş özelliklere sahip yerleşik sınıfları desteklemek için, "for (var key in obj)" yerine getOwnPropertyNames () üzerinde yineleyebilirsiniz
Ronen Ness

3

Bu benim kullandığım derin klonlama yöntemi, bence harika, umarım önerilerde bulunursundur

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

Diğerlerinin bu ve benzeri sorularda belirttiği gibi, genel anlamda bir "nesneyi" klonlamak JavaScript'te şüphelidir.

Bununla birlikte, "veri" nesneleri olarak adlandırdığım, yani basitçe { ... }değişmez değerlerden ve / veya basit özellik atamalarından oluşturulmuş veya JSON'dan serileştirilmemiş hale getirilmiş nesneler olarak adlandırdığım bir nesne sınıfı vardır ve bunlar için klonlanmayı istemenin mantıklıdır. Sadece bugün, büyük bir veri kümesi için ne olduğunu test etmek için bir sunucudan alınan verileri yapay olarak 5 kat şişirmek istedim, ancak nesnenin (bir dizi) ve onun çocuklarının, şeylerin doğru çalışması için farklı nesneler olması gerekiyordu. Klonlama, veri kümemi çarpmam için bunu yapmama izin verdi:

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

Sıklıkla veri nesnelerini klonlamayı bitirdiğim diğer yer, verileri göndermeden önce veri modelindeki nesneden durum alanlarını çıkarmak istediğim ana bilgisayara geri göndermek için. Örneğin, "_" ile başlayan tüm alanları klonlanırken nesneden çıkarmak isteyebilirim.

Bu, bunu genel olarak yapmak için yazdığım koddur, destekleyici diziler ve hangi üyelerin klonlanacağını seçmek için bir seçici (bağlamı belirlemek için bir "yol" dizesi kullanır):

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

Boş olmayan bir kök nesne varsayılarak ve üye seçimi olmadan en basit makul derin klon çözümü şudur:

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

Artık Underscore.js'nin bir üst kümesi olan Lo-Dash , birkaç derin klon işlevine sahiptir:

Bir itibaren yazarın cevabı kendisi:

lodash underscore Underscore'un en son kararlı sürümüyle uyumluluğu sağlamak için build sağlanmıştır.


soru "kütüphaneleri kullanmak istemiyorum" dedi
Femi Oni

@FemiOni soru kütüphaneler hakkında hiçbir şey içermiyor (eski düzenlemelerinde bile) ... Buradaki diğer birkaç cevap da bir kütüphaneyi veya diğerini kullanıyor.
CPHPython

@FemiOni cevabı dün olumsuz oylandı. Merak ediyorum ... Her neyse, burası bir öğrenme yeri ve birisinin gerçekten derin klonun kendisini uygulaması durumunda, lodash kaynak kodubaseClone bazı fikirler sağlayabilir.
CPHPython

@FemiOni JSON nesnesi ne bir kitaplık ne de bir çerçeve ... Eğer işlevi uygulayacaksanız, açık kaynak kitaplıklarından birine bakmanızı ve ihtiyacınız olan parçaları kullanmanızı öneririm (çoğu yıllardır test edilmiştir). Uzun vadede hataları ve gözden kaçan hususları önleyecektir.
CPHPython

2

Aşağıdaki işlev, javascript nesnelerini derinlemesine klonlamanın en etkili yoludur.

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
İddianızı destekleyecek herhangi bir iddianız var mı?
Fabian von Ellerts

Fonksiyonun altında bir örnek verdim. Hiç şüphen var mı?
Kooldandy

Bu örnekler, işlevin çalıştığını gösteriyor, bu harika ama neden "en verimli yol"?
Fabian von Ellerts

Çünkü nesnenin niteliklerini tek döngüde özyinelemeli olarak kopyalıyor. Ayrıca Tarih, İşlev, Nesne, Dizi, Sayı, Dize tüm bunlar doğru şekilde işlenir. Başka bir yolunuz var mı?
Kooldandy

2

Artık böyle bir işleve gerçek dünyada ihtiyaç duyulmamalıdır. Bu sadece akademik bir ilgi.

Tamamen bir egzersiz olarak, bu, onu yapmanın daha işlevsel bir yoludur. @ Tfmontague'in cevabının bir uzantısı, oraya bir koruma bloğu eklemeyi önerdiğim gibi . Ama ES6'ya mecbur hissettiğimi ve her şeyi işlevselleştirdiğimi görünce, işte benim pezevenk versiyonum. Dizi üzerinde eşleme yapmanız ve nesneyi küçültmeniz gerektiği için mantığı karmaşıklaştırır, ancak herhangi bir mutasyonu önler.

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

Haritanın özel muamele gerektirmesi gerektiğini fark ettim, bu nedenle bu ileti dizisindeki tüm önerilerle birlikte kod:

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

tüm cevaplara eklemem

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

Bu diziler, nesneler ve ilkeller için işe yarar. İki geçiş yöntemi arasında geçiş yapan çift özyinelemeli algoritma:

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 yapmak için özyinelemeyi kullanabiliriz. Dizi, nesne, nesne dizisi, işlevli nesne kopyalarını oluşturabilir. İsterseniz harita vb. gibi diğer veri yapısı türleri için işlev ekleyebilirsiniz.

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

İmmutable JS kullanın

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

Veya lodash / birleştirme

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

Dairesel referans kullanan bu benim için çalışıyor

 //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 = new Date (this.oldDate); This.oldDate'den eskiDate'i çalıştırıp newDate oluşturuyordum ama this.oldDate'i de değiştiriyordu, bu yüzden o çözümü kullandım ve işe yaradı.


0

Bu çözüm, [... target] veya {... target} kullanırken yineleme sorunlarını önleyecektir

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
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.