Nodejs: Bir nesne nasıl klonlanır


91

Bir diziyi klonlarsam, kullanırım cloneArr = arr.slice()

Nodejs'de bir nesneyi nasıl klonlayacağımı bilmek istiyorum.

Yanıtlar:


178

Her performans düşüşünü sıkıştırmaya gerek olmayan araçlar ve sınıflar için, genellikle hile yapıyorum ve derin bir kopya gerçekleştirmek için JSON kullanıyorum:

function clone(a) {
   return JSON.parse(JSON.stringify(a));
}

Tek cevap ya da en zarif cevap bu değil; diğer tüm cevaplar üretim darboğazları için düşünülmelidir. Bununla birlikte, bu hızlı ve kirli bir çözüm, oldukça etkili ve basit bir özellik karmasını klonlayacağım çoğu durumda kullanışlıdır.


2
@djechlin Elbette var. Deneyin: jsfiddle.net/katowulf/E5jC3 ( 0.10.11 düğümü ile test edildi) İşlevleri veya prototip verileri yeniden oluşturamayacak, ancak etrafındaki değerleri gayet iyi alıyor.
Kato

13
Bu, tarihleri ​​dizelere dönüştürecektir
geriye dönük

4
@ Backus yanı sıra nesneler ve işlevler.
Kato

2
@Kato, Neden döngüsel referanslar derin kopyalar yapmaya gerek olmadığını ima ediyor? Bunlar birbiriyle alakasız iki şey. NodeJS için döngüsel başvuruları destekleyen ve klonlamak için JSON kullanmaya geri dönmeyen iyi bir klon yöntemi yazmak kesinlikle mümkündür. github.com/pvorb/node-clone
Samuel Neff

2
Zaten yorumlarda ve açıklamada not edildi. Bir işlevi, nesne prototipini veya genel bir klon yöntemiyle yapmaya çalışmamanız gereken diğer şeyleri klonlayamazsınız. Bunun için birden fazla kod satırına ihtiyacınız olacak ve muhtemelen o çöpü klonlamamalısınız - sınıfınıza iç newObj = obj.clone(args...);
Kato

35

Object.assign yukarıdaki cevapların hiçbirinde belirtilmemiştir.

let cloned = Object.assign({}, source);

ES6 kullanıyorsanız, yayma operatörünü kullanabilirsiniz:

let cloned = { ... source };

Referans: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign


29
Bunların her ikisinin de sığ klonlar olduğunu ve nesneler üzerindeki yayılma operatörünün node.js 8+ gerektirdiğini unutmayın.
jlh

1
@Jlh'in bahsettiği gibi, bu iç içe geçmiş nesneler için işe yaramayacak. Bu yöntemin derin klon sorunları hakkında daha fazla
bilgiyi

Shnd'nin dediği gibi, bu cevap yanlış. Derin klonlama için başka alternatifler kullanmamız gerekir çünkü Object.assign () özellik değerlerini kopyalar. Kaynak değeri bir nesneye başvuruysa, yalnızca bu referans değerini kopyalar.
Troy

1
Bu klonlanmaz, sadece orijinal nesneye atıfta bulunur, yani bir şeyi değiştirirseniz ustaya gider
Aidan Welch

@AidanWelch Bunun doğru olduğunu düşünmüyorum. let a = {x:1}; let b = Object.assign({}, a); a.x = 2; a.y = 1; console.log(a, b);
Miguel Pynto

20

Dışarıda "kendi modunuzu almak" istemiyorsanız, bazı Node modülleri var. Bu iyi görünüyor: https://www.npmjs.com/package/clone

Görünüşe göre döngüsel referanslar dahil her türlü şeyi ele alıyor. Gönderen github sayfasından:

klon yöneticileri nesneleri, dizileri, Tarih nesnelerini ve RegEx nesnelerini klonlar. Örneğin, nesnelerdeki dizilerdeki tarihleri ​​klonlayabilmeniz için her şey yinelemeli olarak klonlanır. [...] Dairesel referanslar? Evet!


10

Genel ama yararlı bir klonlama işlemi yapmak zordur çünkü neyin özyinelemeli olarak klonlanması ve neyin kopyalanması gerektiği, belirli nesnenin nasıl çalışacağına bağlıdır.

Yararlı olabilecek bir şey şudur:

function clone(x)
{
    if (x === null || x === undefined)
        return x;
    if (typeof x.clone === "function")
        return x.clone();
    if (x.constructor == Array)
    {
        var r = [];
        for (var i=0,n=x.length; i<n; i++)
            r.push(clone(x[i]));
        return r;
    }
    return x;
}

Bu kodda mantık şu şekildedir:

  • aynı olması durumunda nullveya undefinedsadece geri döndüğünde (özel durum gereklidir çünkü bir cloneyöntemin mevcut olup olmadığını görmeye çalışmak bir hatadır )
  • nesnenin bir cloneyöntemi var mı? o zaman onu kullan
  • nesne bir dizi mi? sonra yinelemeli bir klonlama işlemi yapın
  • aksi halde aynı değeri döndür

Bu klon işlevi, özel klon yöntemlerinin kolayca uygulanmasına izin vermelidir ... örneğin

function Point(x, y)
{
    this.x = x;
    this.y = y;

    ...
}

Point.prototype.clone = function()
{
    return new Point(this.x, this.y);
};



function Polygon(points, style)
{
    this.points = points;
    this.style = style;

    ...
}

Polygon.prototype.clone = function()
{
    return new Polygon(clone(this.points),
                       this.style);
};

Nesnede, belirli bir dizi için doğru bir klonlama işleminin sadece sığ bir kopya olduğunu bildiğinizde, values.slice()bunun yerine çağırabilirsiniz clone(values).

Örneğin yukarıdaki kodda, bir çokgen nesnesinin klonlanmasının noktaları klonlamasını, ancak aynı stil nesnesini paylaşmasını açıkça talep ediyorum. Bunun yerine stil nesnesini de klonlamak istersem, geçebilirim clone(this.style).


1
+1, nesnelerin .cloneyöntemi kendilerinin uygulaması gerektiğinden bahsederken Nesneleri klonlamanın en iyi yolu budur.
Raynos

3
if (x.clone)olmalıif (typeof x.clone === 'function')
Yanick Rochon

@YanickRochon: Teşekkürler, düzeltildi. Pardon, bir şekilde bu yorumu daha önce fark etmemiştim ...
6502

9

Nesneleri klonlamak için yerel bir yöntem yoktur. Alt çizgi _.clonesığ bir klon olan uygular .

_.clone = function(obj) {
  return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

Ya dilimler ya da genişletir.

İşte _.extend

// extend the obj (first parameter)
_.extend = function(obj) {
  // for each other parameter
  each(slice.call(arguments, 1), function(source) {
    // loop through all properties of the other objects
    for (var prop in source) {
      // if the property is not undefined then add it to the object.
      if (source[prop] !== void 0) obj[prop] = source[prop];
    }
  });
  // return the object (first parameter)
  return obj;
};

Genişlet, tüm öğeleri yineler ve içindeki öğelerle yeni bir nesne oluşturur.

İsterseniz kendi saf uygulamanızı sunabilirsiniz.

function clone(o) {
  var ret = {};
  Object.keys(o).forEach(function (val) {
    ret[val] = o[val];
  });
  return ret;
}

Kapaklar klonlanamadığından derin klonlamadan kaçınmak için iyi nedenler vardır.

Şahsen hakkında bir soru sordum deep cloning objects beforeve vardığım sonuç, bunu yapmadığınızdır.

Benim tavsiyem kullanım underscoreve _.clonesığ klonlar için yöntem


9

Sığ bir kopya için, azaltma modelini kullanmayı seviyorum (genellikle bir modülde veya benzeri), şöyle:

var newObject = Object.keys(original).reduce(function (obj, item) {
    obj[item] = original[item];
    return obj;
},{});

İşte birkaç seçenek için bir jsperf: http://jsperf.com/shallow-copying


sade, zarif, parlak. Diğer SO katkılarınızı dört gözle bekliyorum ^ _ ^
Teşekkürler

7

Eski soru, ancak şimdiye kadar önerilenden daha zarif bir cevap var; yerleşik araçları kullanın._extend:

var extend = require("util")._extend;

var varToCopy = { test: 12345, nested: { val: 6789 } };

var copiedObject = extend({}, varToCopy);

console.log(copiedObject);

// outputs:
// { test: 12345, nested: { val: 6789 } }

İlk parametrenin boş bir nesneyle kullanımına dikkat edin {} - bu, kopyalanan nesnenin / nesnelerin yeni bir nesneye kopyalanması gerektiğini söyler. Birinci parametre olarak mevcut bir nesneyi kullanırsanız, ikinci (ve sonraki tüm) parametreler, birinci parametre değişkeni üzerinde derin birleştirme-kopyalanacaktır.

Yukarıdaki örnek değişkenleri kullanarak şunu da yapabilirsiniz:

var anotherMergeVar = { foo: "bar" };

extend(copiedObject, { anotherParam: 'value' }, anotherMergeVar);

console.log(copiedObject);

// outputs:
// { test: 12345, nested: { val: 6789 }, anotherParam: 'value', foo: 'bar' }

Özellikle AngularJS ve jQuery'de genişletmeye alışkın olduğum yerlerde çok kullanışlı bir yardımcı program.

Umarım bu başka birine yardımcı olur; nesne referansının üzerine yazılması bir ızdıraptır ve bu her seferinde çözer!


1
_Extend kullanımı artık amortismana tabi tutulmuştur. Bunun yerine
Object.assign

7

Lodash'ı da kullanabilirsiniz . Bir sahiptir klon ve cloneDeep yöntemleri.

var _= require('lodash');

var objects = [{ 'a': 1 }, { 'b': 2 }];

var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]);
// => true

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);

1

Klonlanan nesnenizle ne yapmak istediğinize bağlı olarak, javascript'in prototip kalıtım mekanizmasını kullanabilir ve aşağıdakiler aracılığıyla bir şekilde klonlanmış bir nesne elde edebilirsiniz:

var clonedObject = Object.create(originalObject);

Sadece bunun tam bir klon olmadığını unutmayın - iyi ya da kötü.

Bununla ilgili iyi bir şey, nesneyi aslında kopyalamamış olmanızdır, bu nedenle bellek ayak izi düşük olacaktır.

Bu yöntemle ilgili hatırlanması gereken bazı yanıltıcı şeyler, prototip zincirinde tanımlanan özelliklerin yinelemesinin bazen biraz farklı çalıştığı ve orijinal nesnedeki herhangi bir değişikliğin klonlanan nesneyi de etkileyeceği gerçeğidir. .


3
Bu çok gülünç derecede tehlikeli ve bir klona hiç benzemiyor. var a = {foo: "bar"}, b = Object.create(a); a.foo = "broken"; console.log(b.foo); // "broken";
Teşekkürler

@naomik: b.foo = "working"; console.log(a.foo); // still "broken";Elbette, orijinal nesnedeki değişikliklerin "klon" a yansıtılacağının ve "klon" daki değişikliklerin orijinal nesneye yansıtılmayacağının farkında olmalı - ama ben buna tehlikeli demem - hepsi
klonunuzla

@naomik: JS'de oldukça yeniyim, bu yüzden bazı kesinlikler istiyorum. Olli-k tarafından önerilen ve sizin beğendiğiniz sığ klon (IMHO, ama yanılıyor olabilirim), voxpelli tarafından önerilen çözümle aynı sınırlamalara sahip görünüyor. Orijinal ve sığ kapanış da aynı "öğeleri" paylaşacak, orijinaldeki bir öğeyi güncellemek sığ klonu da etkileyecektir. Veya?
bmorin

Eğer Olli K'nın tekniğini kullanmak durumunda @bmornin, orijinal nesneyi değiştirerek edecek değil yeni nesnede bir değişime yol açmıştır.
Teşekkürler

1
@VoxPelli Bir "klon" bütün amacı özgün nesneden izole değişikliklere bağlıdır, bu nedenle bu yöntem, bir daha tehlikeli.
Doug

1

Tam bir derin kopya uyguladım. Genel bir klon yöntemi için en iyi seçim olduğuna inanıyorum, ancak döngüsel referansları işlemez.

Kullanım örneği:

parent = {'prop_chain':3}
obj = Object.create(parent)
obj.a=0; obj.b=1; obj.c=2;

obj2 = copy(obj)

console.log(obj, obj.prop_chain)
// '{'a':0, 'b':1, 'c':2} 3
console.log(obj2, obj2.prop_chain)
// '{'a':0, 'b':1, 'c':2} 3

parent.prop_chain=4
obj2.a = 15

console.log(obj, obj.prop_chain)
// '{'a':0, 'b':1, 'c':2} 4
console.log(obj2, obj2.prop_chain)
// '{'a':15, 'b':1, 'c':2} 4

Kodun kendisi:

Bu kod, nesneleri prototipleriyle kopyalar, aynı zamanda işlevleri de kopyalar (birisi için yararlı olabilir).

function copy(obj) {
  // (F.prototype will hold the object prototype chain)
  function F() {}
  var newObj;

  if(typeof obj.clone === 'function')
    return obj.clone()

  // To copy something that is not an object, just return it:
  if(typeof obj !== 'object' && typeof obj !== 'function' || obj == null)
    return obj;

  if(typeof obj === 'object') {    
    // Copy the prototype:
    newObj = {}
    var proto = Object.getPrototypeOf(obj)
    Object.setPrototypeOf(newObj, proto)
  } else {
    // If the object is a function the function evaluate it:
    var aux
    newObj = eval('aux='+obj.toString())
    // And copy the prototype:
    newObj.prototype = obj.prototype
  }

  // Copy the object normal properties with a deep copy:
  for(var i in obj) {
    if(obj.hasOwnProperty(i)) {
      if(typeof obj[i] !== 'object')
        newObj[i] = obj[i]
      else
        newObj[i] = copy(obj[i])
    }
  }

  return newObj;
}

Bu nüsha ile orijinal ile kopyalanan arasında orijinalin yapımında kapaklar kullanması dışında bir fark bulamıyorum, bu yüzden iyi bir uygulama olduğunu düşünüyorum.

Umut ediyorum bu yardım eder


1

dizi için kullanılabilir

var arr = [1,2,3]; 
var arr_2 = arr ; 

print ( arr_2 ); 

arr = dizi. dilim (0);

print ( arr ); 

arr[1]=9999; 

print ( arr_2 ); 

1

JavaScript'teki Nesneler ve Diziler başvuruya göre çağrı kullanır, kopyalanan değeri güncellerseniz orijinal nesneye yansıyabilir. Bunu önlemek için, lodash library cloneDeep yöntemi run komutunu kullanarak başvurunun geçilmesini önlemek için nesneyi derinlemesine klonlayabilirsiniz.

npm lodash yükle

const ld = require('lodash')
const objectToCopy = {name: "john", age: 24}
const clonedObject = ld.cloneDeep(objectToCopy)
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.