Alt çizgi kullanarak bir dizi nesneyi nasıl klonlarsınız?


82
#!/usr/bin/env node
var _ = require('underscore');
var a = [{f: 1}, {f:5}, {f:10}];
var b = _.clone(a);
b[1].f = 55;
console.log(JSON.stringify(a));

Bunun sonucu:

[{"f":1},{"f":55},{"f":10}]

Klon çalışmıyor gibi görünüyor! Bu yüzden RTFM ve şunu görüyorum:

http://underscorejs.org/#clone

Nesnenin sığ kopyalanmış bir klonunu oluşturun. İç içe geçmiş nesneler veya diziler, çoğaltılmayacak, referans olarak kopyalanacaktır.

Yani _.cloneoldukça işe yaramaz. Nesne dizisini gerçekten kopyalamanın bir yolu var mı?


4
Reddedilen derin kopya için bir çekme isteği vardı: github.com/jashkenas/underscore/pull/595 Lo-Dash cloneDeep'e sahip
epascarello

5
lol Ben sadece alt çizgi üzerindeki kelime oyununu fark ettim. Düşük çizgi.
Jess

Yanıtlar:


122

Bir numara var! Klon, iç içe geçmiş nesneleri "klonlamaz "sa, her nesneyi bir harita çağrısı içinde açıkça klonlayarak bunu yapmaya zorlayabilirsiniz! Bunun gibi:

#!/usr/bin/env node
var _ = require('underscore');
var a = [{f: 1}, {f:5}, {f:10}];
var b = _.map(a, _.clone);       // <----
b[1].f = 55;
console.log(JSON.stringify(a));

Baskılar:

[{"f":1},{"f":5},{"f":10}]

Yaşasın! adeğişmedi! Artık bkendi zevkime göre düzenleme yapabilirim !


48
Yine de dikkatli olun. Bu elbette sadece iki seviye derinlikte çalışır. bu örnekten daha fazla iç içe geçmiş diziler veya nesneler için değil.
Simon Zyx

1
Tasarım gereği, Alt Çizgi de RegExp veya Date değerlerini doğru şekilde klonlamaz
Mark K Cowan

1
Buraya bakan herkes aşağıdaki cevabımı görmeli.
gdibble

65

Github'daki sorundan çıkarılan , herhangi bir düzeydeki iç içe veri ile çalışan ve alt çizgi gerektirmeyen başka bir çözüm :

JSON.parse(JSON.stringify(obj))

13
Bu, nesnenin bir döngüsü olmadığı sürece çalışır ve bu durumda JSON.stringifybir hata verir. Orijinalde durum böyle değil ama yine de ilginç bir durum. a = {simple: 'thing'}; a.cycle = a ; JSON.stringify(a).
mcdave

10
Ayrıca, bu çözümün yalnızca basit türleri olan nesneler için işe yaradığını belirtmek gerekir. Örneğin, nesne Dateveya Regexörnekleriniz varsa, bunlar dizelere göre serileştirilecektir. Dünyanın sonu değil, ancak bunu kullanıyorsanız ve Dateörnekler bekliyorsanız, bunu halletmeniz gerekir .
cayleyh

1
Ve eğer birisinin bunu deneyip besleyebileceğini düşünüyorsanız, undefinedisteyeceksiniz JSON.parse(JSON.stringify(obj) || null)aksi halde bir hata verecektir.
Ian Mackinnon

1
@Cayleyh'in bahsettiği şeyle birlikte, bu, ' functionleri tamamen düşürecek .
Marko Grešak


9

Alt Çizgi API referansı :

_.toArray(list)Listeden gerçek bir Dizi oluşturur (tekrarlanabilen her şey). Arguments nesnesini dönüştürmek için kullanışlıdır.

... veya bu durumda, bir diziyi klonlamak . Bunu dene:

var _ = require('underscore');
var array1 =  [{a:{b:{c:1}}},{b:{c:{a:2}}},{c:{a:{b:3}}}];
var array2 = _.toArray(array1);
console.log(array1 === array2); --> false
console.log(array1[0] === array2[0]); --> true

Aşağıda, Steve'in aşağıdaki yorumundan sonra oluşturduğum bir ek var -thx

Vanilla JS (veya _.cloneistenirse kullanarak ) derin klonlama özyinelemeli yardımcısı :

function clone(thing, opts) {
    var newObject = {};
    if (thing instanceof Array) {
        return thing.map(function (i) { return clone(i, opts); });
    } else if (thing instanceof Date) {
        return new Date(thing);
    } else if (thing instanceof RegExp) {
        return new RegExp(thing);
    } else if (thing instanceof Function) {
        return opts && opts.newFns ?
                   new Function('return ' + thing.toString())() :
                   thing;
    } else if (thing instanceof Object) {
        Object.keys(thing).forEach(function (key) {
            newObject[key] = clone(thing[key], opts);
        });
        return newObject;
    } else if ([ undefined, null ].indexOf(thing) > -1) {
        return thing;
    } else {
        if (thing.constructor.name === 'Symbol') {
            return Symbol(thing.toString()
                       .replace(/^Symbol\(/, '')
                       .slice(0, -1));
        }
        // return _.clone(thing);  // If you must use _ ;)
        return thing.__proto__.constructor(thing);
    }
}

var a = {
    a: undefined,
    b: null,
    c: 'a',
    d: 0,
    e: Symbol('a'),
    f: {},
    g: { a:1 },
    h: [],
    i: [ { a:2 }, { a:3 } ],
    j: [ 1, 2 ],
    k: function (a) { return a; },
    l: /[a-z]/g,
    z: [ {
        a: undefined,
        b: null,
        c: 'b',
        d: 1,
        e: Symbol(1),
        f: {},
        g: { b:2 },
        h: { c:{ c:3 } },
        i: { a:Symbol('b') },
        j: { a:undefined, b:null },
        k: [],
        l: [ 1, [ 1, 2 ], [ [ 1, 2, 3 ] ] ],
        m: function (a) { return !a; },
        n: { a:function (a) { return !!a; } },
        o: /(a|b)/i
       } ]
};
var b = clone(a);
var c = clone(a, { newFns:true });


/* Results - value beneath each for reference:

a.a === b.a --> true
undefined

a.b === b.b --> true
null

a.c === b.c --> true
'a'

a.d === b.d --> true
0

a.e === b.e --> false
Symbol(a)

a.f === b.f --> false
{}

a.g === b.g --> false
{ a:1 }

a.h === b.h --> false
[]

a.i === b.i --> false
[ { a:2 }, { a:3 } ]

a.i[0] === b.i[0] --> false
{ a:2 }

a.i[0].a === b.i[0].a --> true
2

a.j === b.j --> false
[ 1, 2 ]

a.k === b.k --> true
a.k === c.k --> false
function (a) { return a; }

a.l === b.l --> false
/[a-z]/g

a.z === b.z --> false
[Object]

a.z[0].a === b.z[0].a --> true
undefined

a.z[0].b === b.z[0].b --> true
null

a.z[0].c === b.z[0].c --> true
'b'

a.z[0].d === b.z[0].d --> true
1

a.z[0].e === b.z[0].e --> 
false
Symbol(1)

a.z[0].f === b.z[0].f --> false
{}

a.z[0].g === b.z[0].g -- > false
{ b:2 }

a.z[0].g.b === b.z[0].g.b --> true
2

a.z[0].h === b.z[0].h --> false
{ c:{ c:3 } }

a.z[0].h.c === b.z[0].h.c --> false
{ c:3 }

a.z[0].h.c.c === b.z[0].h.c.c --> true
3

a.z[0].i === b.z[0].i --> false
{ a:Symbol(b) }

a.z[0].i.a === b.z[0].i.a --> false
Symbol(b)

a.z[0].j === b.z[0].j --> false
{ a:undefined, b:null }

a.z[0].j.a === b.z[0].j.a --> true
undefined

a.z[0].k === b.z[0].k --> false
[]

a.z[0].l === b.z[0].l --> false
[ 1, [ 1, 2 ], [ [ 1, 2, 3 ] ] ]

a.z[0].l[1] === b.z[0].l[1] --> false
[ 1, 2 ]

a.z[0].l[1][1] === b.z[0].l[1][1] --> true
2

a.z[0].m === b.z[0].m --> true
a.z[0].m === c.z[0].m --> false
function (a) { return !a; }

a.z[0].n === b.z[0].n --> false
{ a:function (a) { return !!a; } }

a.z[0].n.a === b.z[0].n.a --> true
a.z[0].n.a === c.z[0].n.a --> false
function (a) { return !!a; }

a.z[0].o === b.z[0].o --> false
/(a|b)/i

*/

Bu en iyi cevap.
Pierre

_.toArray(list)bir dizideki nesneleri klonlamaz. var array1 = [{a: 1}, {a: 2}, {a: 3}]; var array2 = _.toArray(array1); array2[0].a = 999; console.log(array1[0]); --> {a: 999}
Steve Lang

@SteveLang bunu belirttiğiniz için teşekkürler. Oops. Bu nedenle, yukarıdaki vanilya JS _.cloneelse
fn'yi
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.