Cara Deep clone di javascript

105

Bagaimana Anda mengkloning objek Javascript secara mendalam?

Saya tahu ada berbagai fungsi berdasarkan kerangka kerja seperti JSON.parse(JSON.stringify(o))dan $.extend(true, {}, o)tetapi saya tidak ingin menggunakan kerangka seperti itu.

Apa cara paling elegan atau efisien untuk membuat klon yang dalam.

Kami sangat peduli dengan kasus tepi seperti kloning array. Tidak merusak rantai prototipe, berurusan dengan referensi diri.

Kami tidak peduli tentang mendukung penyalinan objek DOM dan sejenisnya karena .cloneNodeada karena alasan itu.

Karena saya terutama ingin menggunakan klon dalam node.jsmenggunakan fitur ES5 dari mesin V8 dapat diterima.

[Sunting]

Sebelum ada yang menyarankan izinkan saya menyebutkan ada perbedaan yang jelas antara membuat salinan dengan secara prototipikal mewarisi dari objek dan mengkloningnya . Yang pertama membuat rantai prototipe menjadi berantakan.

[Edit Lebih Lanjut]

Setelah membaca jawaban Anda, saya mendapatkan penemuan menjengkelkan bahwa mengkloning seluruh objek adalah permainan yang sangat berbahaya dan sulit. Ambil contoh objek berbasis closure berikut

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

Apakah ada cara untuk menulis fungsi kloning yang mengkloning objek, memiliki status yang sama pada saat kloning tetapi tidak dapat mengubah status otanpa menulis parser JS di JS.

Seharusnya tidak ada kebutuhan dunia nyata untuk fungsi seperti itu lagi. Ini hanya kepentingan akademis.

Raynos
sumber
2
Sebelum ditandai sebagai duplikat, saya melihat stackoverflow.com/questions/122102/… dan tidak menemukan jawaban yang menangani semua kasus tepi.
Raynos
Persyaratan di bagian "edit lebih lanjut" tidak mungkin dicapai tanpa "bantuan" dari objek itu sendiri, karena variabel privat tersebut benar-benar privat , dan akibatnya tidak dapat diakses oleh clonefungsi generik . Objek yang dipermasalahkan harus memperlihatkan clonemetode yang dibuat khusus sendiri .
trincot
Saya telah banyak membaca tentang ini malam ini, dan di antara sumber daya yang saya temukan adalah entri blog yang tampak jelek ini yang menyertakan beberapa peretasan untuk mengakses algoritme klon terstruktur di browser: dassur.ma/things/deep-copy
Kucing
Apakah ini menjawab pertanyaan Anda? Apa cara paling efisien untuk mengkloning objek dalam JavaScript?
GalaxyCat105

Jawaban:

67

Itu sangat tergantung pada apa yang ingin Anda klon. Apakah ini benar-benar objek JSON atau sembarang objek di JavaScript? Jika Anda ingin melakukan klon apa pun, itu mungkin membuat Anda mendapat masalah. Masalah yang mana? Saya akan menjelaskannya di bawah ini, tetapi pertama-tama, contoh kode yang mengkloning literal objek, primitif, array, dan node 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";
            }   
        }
    ]
})

Dan sekarang, mari kita bicara tentang masalah yang mungkin Anda dapatkan saat mulai mengkloning objek REAL. Saya sekarang berbicara tentang objek yang Anda buat dengan melakukan sesuatu seperti

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

Tentu saja Anda dapat mengkloningnya, itu bukan masalah, setiap objek mengekspos properti konstruktor, dan Anda dapat menggunakannya untuk mengkloning objek, tetapi itu tidak akan selalu berfungsi. Anda juga dapat melakukan sederhana for inpada objek ini, tetapi itu mengarah ke arah yang sama - masalah. Saya juga menyertakan fungsionalitas klon di dalam kode, tetapi dikecualikan oleh if( false )pernyataan.

Jadi, mengapa kloning bisa menyebalkan? Pertama-tama, setiap objek / instance mungkin memiliki beberapa status. Anda tidak pernah bisa yakin bahwa objek Anda tidak memiliki, misalnya variabel privat, dan jika demikian halnya, dengan mengkloning objek, Anda hanya memutus status.

Bayangkan tidak ada negara bagian, itu bagus. Lalu kami masih punya masalah lain. Kloning melalui metode "konstruktor" akan memberi kita kendala lain. Ini adalah ketergantungan argumen. Anda tidak pernah bisa yakin, bahwa seseorang yang menciptakan objek ini, tidak melakukannya, semacam

new User({
   bike : someBikeInstance
});

Jika ini masalahnya, Anda kurang beruntung, someBikeInstance mungkin dibuat dalam beberapa konteks dan konteks tersebut tidak diketahui untuk metode klon.

Lalu apa yang harus dilakukan? Anda masih dapat melakukan for insolusi, dan memperlakukan objek seperti itu seperti objek literal normal, tetapi mungkin ide untuk tidak mengkloning objek tersebut sama sekali, dan hanya meneruskan referensi objek ini?

Solusi lainnya adalah - Anda dapat menyetel konvensi bahwa semua objek yang harus digandakan harus mengimplementasikan bagian ini sendiri dan menyediakan metode API yang sesuai (seperti cloneObject). Sesuatu yang cloneNodedilakukan untuk DOM.

Kamu putuskan.

nemisj
sumber
Saya datang pada rintangan berurusan dengan objek yang menggunakan penutupan untuk menyembunyikan keadaan diri saya. Bagaimana seseorang bisa mengkloning sebuah objek dan seluruh statusnya tetapi tetap memastikan klon tidak dapat mengubah status aslinya dengan sendirinya. Sebuah titik couinter result = new item.constructor();menjadi buruk adalah bahwa mengingat fungsi konstruktor & objek item Anda harus dapat RE setiap parameter yang diteruskan ke konstruktor.
Raynos
7
@Raynos: jika objek menggunakan penutupan untuk menyembunyikan status, Anda tidak dapat mengkloningnya. Karenanya istilah 'penutupan'. Seperti yang dikatakan nemisj di bagian akhir, cara terbaik adalah mengimplementasikan metode API untuk kloning (atau serialisasi / deserialisasi) jika itu adalah opsi.
Michiel Kalkman
@MichielKalkman Saya merasa itulah masalahnya. Meskipun seseorang mungkin memiliki solusi yang sangat pintar untuk ini.
Raynos
Apa efeknya false && item.constructor? Bukankah itu tidak ifberguna?
Gabriel Petrovay
2
@GabrielPetrovay Itu if'tidak berguna' dari perspektif fungsional, karena itu tidak akan pernah berjalan, tetapi memiliki tujuan akademis untuk menunjukkan implementasi hipotetis yang mungkin coba digunakan, yang tidak disarankan oleh penulis karena alasan yang dijelaskan nanti. Jadi, ya, itu akan memicu elseklausa setiap kali kondisi dievaluasi, namun ada alasan kode tersebut ada di sana.
Gui Imamura
155

Cara yang sangat sederhana, mungkin terlalu sederhana:

var cloned = JSON.parse(JSON.stringify(objectToClone));
G. Ghez
sumber
12
Hebat kecuali nilai objek adalah fungsi, pada titik mana Anda harus menggunakan sesuatu seperti jawaban yang diterima. Atau gunakan fungsi pembantu seperti cloneDeepdi Lodash.
matthoil dan
31
Jika nilai objek adalah fungsi, objek tersebut bukan JSON.
Jos de Jong
5
Kasus penggunaan apa yang dapat membenarkan untuk mengkloning suatu fungsi daripada hanya menggunakannya?
G. Ghez
3
Jika saya ingat dengan benar ini juga mengubah tanggal menjadi string.
Peter
3
@ G.Ghez jika Anda mengkloning objek yang berisi fungsi yang fungsinya akan hilang ..
Peter
38

The JSON.parse(JSON.stringify())kombinasi untuk salinan yang mendalam Javascript objek adalah hack tidak efektif, karena itu dimaksudkan untuk data JSON. Ini tidak mendukung nilai undefinedatau function () {}, dan akan mengabaikannya (atau nullmereka) saat "merangkai" (menyusun) objek Javascript ke JSON.

Solusi yang lebih baik adalah dengan menggunakan fungsi deep copy. Fungsi di bawah menyalin objek secara mendalam, dan tidak memerlukan pustaka pihak ketiga (jQuery, LoDash, dll).

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;
}
tfmontague
sumber
7
Kecuali jika aObject (atau objek lain di dalamnya) berisi referensi sendiri ke dirinya sendiri ... stackoverflow ™!
e2-e4
@ ringø - Dapatkah Anda memberikan beberapa kasus uji "referensi sendiri"?
tfmontague
4
var o = { a:1, b:2 } ; o["oo"] = { c:3, m:o };
e2-e4
4
Saya suka solusi ini. Satu-satunya perbaikan bagi saya adalah berurusan dengan nilai nol: bObject[k] = (v === null) ? null : (typeof v === "object") ? copy(v) : v;
David Kirkland
2
Fungsi ini sederhana, dapat dimengerti dan akan menangkap hampir semua kasus. Di dunia JavaScript, itu sesempurna yang bisa Anda dapatkan.
icc97
21

Berikut adalah fungsi ES6 yang juga akan berfungsi untuk objek dengan referensi siklik:

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

Catatan tentang Set dan Peta

Bagaimana menangani kunci Sets dan Maps masih bisa diperdebatkan: kunci-kunci itu seringkali primitif (dalam hal ini tidak ada perdebatan), tetapi mereka juga bisa menjadi objek. Dalam hal ini pertanyaannya menjadi: haruskah kunci-kunci itu digandakan?

Orang dapat berargumen bahwa ini harus dilakukan, sehingga jika objek tersebut bermutasi dalam salinan, objek dalam aslinya tidak terpengaruh, dan sebaliknya.

Di sisi lain orang akan menginginkan bahwa jika sebuah Set / Map hassebuah kunci, ini harus benar baik pada aslinya maupun salinannya - setidaknya sebelum perubahan apapun dilakukan pada salah satu dari mereka. Akan aneh jika salinannya adalah Set / Map yang memiliki kunci yang tidak pernah terjadi sebelumnya (seperti yang dibuat selama proses kloning): tentunya itu tidak terlalu berguna untuk kode apa pun yang perlu mengetahui apakah objek yang diberikan adalah a kunci Set / Map itu atau tidak.

Seperti yang Anda perhatikan, saya lebih berpendapat kedua: kunci Sets dan Maps adalah nilai (mungkin referensi ) yang harus tetap sama.

Pilihan seperti itu sering juga muncul dengan objek lain (mungkin khusus). Tidak ada solusi umum, karena banyak bergantung pada bagaimana objek kloning diharapkan berperilaku dalam kasus spesifik Anda.

trincot
sumber
tidak menangani tanggal dan
ekspresi
1
@mkeremguc, terima kasih atas komentar Anda. Saya memperbarui kode untuk mendukung tanggal dan regexp.
trincot
Solusi bagus termasuk peta. Hanya kehilangan dukungan untuk kloning set es6.
Robert Biggs
Jika saya tidak salah, Anda mungkin dapat menambahkan dukungan untuk Sets dengan:if (object instanceof Set) Array.from(object, val => result.add(deepClone(val, hash)));
Robert Biggs
@RobertBiggs, itu adalah satu kemungkinan, tetapi menurut saya jika sebuah Set memiliki kunci tertentu, itu juga harus benar dalam versi kloning dari Set tersebut. Dengan kode yang Anda sarankan, ini tidak akan berlaku jika kuncinya adalah objek. Oleh karena itu saya menyarankan untuk tidak mengkloning kunci - saya pikir itu akan berperilaku lebih seperti yang diharapkan. Lihat update jawaban saya dalam hal itu.
trincot
10

The Underscore.js contrib perpustakaan perpustakaan memiliki fungsi yang disebut snapshot bahwa klon yang mendalam objek

cuplikan dari sumber:

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

setelah pustaka ditautkan ke proyek Anda, panggil fungsi dengan mudah

_.snapshot(object);
svarog.dll
sumber
4
solusi yang baik, satu hal yang perlu diingat: klon dan aslinya berbagi prototipe yang sama. jika itu masalah, mungkin untuk menambahkan "temp .__ proto__ = .snapshot (obj .__ proto_ );" tepat di atas "return temp", dan untuk mendukung class build-in dengan properti yang ditandai sebagai 'no enumerate' Anda dapat melakukan iterasi pada getOwnPropertyNames () daripada "for (var key in obj)"
Ronen Ness
3

Ini adalah metode kloning mendalam yang saya gunakan, saya pikir itu Hebat, semoga Anda memberi saran

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;
}
Menjahit
sumber
3

Seperti yang telah dicatat orang lain tentang ini dan pertanyaan serupa, mengkloning "objek", dalam arti umum, meragukan di JavaScript.

Namun, ada kelas objek, yang saya sebut objek "data", yaitu, yang dibangun hanya dari { ... }literal dan / atau penetapan properti sederhana atau deserialisasi dari JSON yang masuk akal untuk ingin dikloning. Baru hari ini saya ingin meningkatkan data yang diterima dari server sebesar 5x secara artifisial untuk menguji apa yang terjadi pada kumpulan data yang besar, tetapi objek (array) dan turunannya harus menjadi objek berbeda agar semuanya berfungsi dengan benar. Kloning memungkinkan saya melakukan ini untuk menggandakan kumpulan data saya:

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

Tempat lain yang sering saya gunakan untuk mengkloning objek data adalah untuk mengirimkan data kembali ke host tempat saya ingin menghapus kolom status dari objek dalam model data sebelum mengirimkannya. Misalnya, saya mungkin ingin menghapus semua bidang yang dimulai dengan "_" dari objek saat digandakan.

Ini adalah kode yang akhirnya saya tulis untuk melakukan ini secara umum, termasuk mendukung array dan pemilih untuk memilih anggota mana yang akan dikloning (yang menggunakan string "jalur" untuk menentukan konteks):

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

Solusi deep-clone paling sederhana yang masuk akal, dengan asumsi objek root bukan null dan tanpa pemilihan anggota adalah:

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;
    }
Lawrence Dol
sumber
2

Lo-Dash , sekarang superset dari Underscore.js , memiliki beberapa fungsi klon yang dalam:

Dari jawaban penulis sendiri:

lodash underscore build disediakan untuk memastikan kompatibilitas dengan Underscore versi stabil terbaru.

CPHPython
sumber
Pertanyaan mengatakan "saya tidak ingin menggunakan perpustakaan"
Femi Oni
@FemiOni pertanyaannya tidak memiliki apa-apa tentang perpustakaan (bahkan dalam suntingan lama) ... Beberapa jawaban lain di sini juga menggunakan satu perpustakaan atau lainnya juga.
CPHPython
Jawaban @FemiOni tidak disukai kemarin. Saya ingin tahu ... Bagaimanapun, ini adalah tempat belajar dan jika seseorang benar-benar akan menerapkan klon yang dalam itu sendiri, kode sumber lodashbaseClone dapat memberikan beberapa ide.
CPHPython
@FemiOni objek JSON bukanlah perpustakaan atau kerangka kerja ... Jika Anda akan menerapkan fungsi tersebut, saya sarankan melihat ke salah satu perpustakaan sumber terbuka dan menggunakan bagian yang Anda butuhkan (banyak yang telah diuji selama bertahun-tahun). Ini akan menghindari bug dan pertimbangan yang terlewat dalam jangka panjang.
CPHPython
2

Fungsi di bawah ini adalah cara paling efisien untuk mengkloning objek javascript secara mendalam.

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
Kooldandy
sumber
2
Apakah Anda memiliki argumen untuk mendukung klaim Anda?
Fabian von Ellerts
Saya telah memberikan contoh fungsi di bawah ini. Apakah Anda ragu?
Kooldandy
Contoh tersebut menunjukkan bahwa fungsi tersebut berfungsi, yang mana keren, tetapi mengapa ini "cara yang paling efisien"?
Fabian von Ellerts
Karena itu secara rekursif menyalin atribut objek dalam satu loop. Juga Tanggal, Fungsi, Objek, Array, Angka, String semua ini ditangani dengan benar. Apakah Anda punya cara lain?
Kooldandy
2

Seharusnya tidak ada kebutuhan dunia nyata untuk fungsi seperti itu lagi. Ini hanya kepentingan akademis.

Sebagai murni latihan, ini adalah cara yang lebih fungsional untuk melakukannya. Ini merupakan perpanjangan dari jawaban @ tfmontague seperti yang saya sarankan untuk menambahkan blok penjaga di sana. Tetapi karena saya merasa terdorong untuk menggunakan ES6 dan memfungsikan semua hal, inilah versi pimped saya. Ini memperumit logika karena Anda harus memetakan di atas array dan mengurangi objek, tetapi ini menghindari mutasi apa pun.

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

icc97.dll
sumber
1

Saya perhatikan bahwa Map harus memerlukan perlakuan khusus, jadi dengan semua saran di utas ini, kode akan menjadi:

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;
}
Dmitriy Pichugin
sumber
1

penambahan saya untuk semua jawaban

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
}
Ronald C.
sumber
0

Ini berfungsi untuk array, objek, dan primitif. Algoritme rekursif ganda yang beralih di antara dua metode traversal:

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);
}
Eric Grotke
sumber
0

Kita bisa memanfaatkan rekursi untuk membuat deepCopy. Itu dapat membuat salinan dari array, objek, array objek, objek dengan fungsi. Jika mau, Anda dapat menambahkan fungsi untuk jenis struktur data lain seperti peta, dll.

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;
    };
Tidak ada
sumber
0

Gunakan immutableJS

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

Atau lodash / merge

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 }] }
Alexander Ladonin
sumber
0

Yang ini, menggunakan referensi melingkar, bekerja untuk saya

 //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));
Tom Lecoz
sumber
0

var newDate = new Date (this.oldDate); Saya melewati oldDate untuk berfungsi dan menghasilkan newDate dari this.oldDate, tetapi itu juga mengubah this.oldDate. Jadi saya menggunakan solusi itu dan berhasil.

Tejas Pawar
sumber
0

Solusi ini akan menghindari masalah rekursi saat menggunakan [... target] atau {... 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
}
pbatey.dll
sumber