JavaScript'te bir nesneyi derin klonlamanın en etkili yolu nedir?


5180

Bir JavaScript nesnesini klonlamanın en etkili yolu nedir? obj = eval(uneval(o));Kullanıldığını gördüm , ancak bu standart değil ve sadece Firefox tarafından destekleniyor . Verimliliği sorgulamaktan başka

şeyler yaptım obj = JSON.parse(JSON.stringify(o));.

Ayrıca çeşitli kusurları olan özyinelemeli kopyalama işlevleri gördüm.
Kanonik bir çözüm bulunmamasına şaşırdım.


566
Eval kötü değildir. Eval kötü kullanmak. Yan etkilerinden korkuyorsanız, yanlış kullanıyorsunuz demektir. Korktuğunuz yan etkiler, bunu kullanmanın nedenleridir. Bu arada kimse gerçekten sorunuza cevap verdi mi?
James

15
Nesneleri klonlamak, özellikle keyfi koleksiyonların özel nesneleri ile zor bir iştir. Muhtemelen bunu yapmanın bir yolu yok.
b01

12
eval()genellikle kötü bir fikirdir çünkü birçok Javascript motorunun optimize edicisi, ayarlanan değişkenlerle uğraşırken kapanmak zorundadıreval . Sadece eval()kodunuzu kullanmak daha kötü performansa yol açabilir.
user56reinstatemonica8


12
Not o JSONyöntem JSON hiçbir eşdeğer herhangi JavaScript türlerini kaybedeceksiniz. Örneğin: JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))üretecek{a: null, b: null, c: null, g: false}
oriadam

Yanıtlar:


4732

Doğal derin klonlama

Buna "yapılandırılmış klonlama" denir, Düğüm 11 ve sonrasında deneysel olarak çalışır ve umarım tarayıcılara iner. Daha fazla ayrıntı için bu cevaba bakınız.

Veri kaybı ile hızlı klonlama - JSON.parse / stringify

Eğer kullanmıyorsanız Dateler, fonksiyonlar, undefined, Infinitysenin nesnenin içinde, regexpleri, Haritalar, Kümeler, lekeler, filelists, ImageDatas, seyrek Diziler, Yazılan Diziler veya diğer karmaşık türleri, derin klon çok basit bir liner bir nesnedir:

JSON.parse(JSON.stringify(object))

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
  re: /.*/,  // lost
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()

Corban'ın cevabına bakınKıyaslamalar için .

Bir kütüphane kullanarak güvenilir klonlama

Nesneleri klonlamak önemsiz olmadığından (karmaşık türler, dairesel referanslar, işlev vb.), Çoğu büyük kütüphane nesneleri klonlamak için işlev sağlar. Tekerleği yeniden icat etmeyin - zaten bir kitaplık kullanıyorsanız, nesnenin klonlama işlevi olup olmadığını kontrol edin. Örneğin,

ES6

Tamlık için, ES6'nın iki sığ kopya mekanizması sunduğunu unutmayın: Object.assign()ve forma sözdizimi . numaralandırılabilir tüm özelliklerin değerlerini bir nesneden diğerine kopyalar. Örneğin:

var A1 = {a: "2"};
var A2 = Object.assign({}, A1);
var A3 = {...A1};  // Spread Syntax

7
@ThiefMaster github.com/jquery/jquery/blob/master/src/core.js 276 satırında (başka bir şey yapan biraz kod var ama "bunu JS'de nasıl yapacağınız" kodu var :)
Rune FS

7
İşte jQuery derin kopyasının arkasındaki JS kodu, ilgilenen herkes için: github.com/jquery/jquery/blob/master/src/core.js#L265-327
Alex W

194
Vay! Sadece net olmak gerekirse: Bu yanıtın neden doğru cevap olarak seçildiğine dair hiçbir fikrim yok, bu aşağıda verilen yanıtlara bir cevaptı: stackoverflow.com/a/122190/6524 (ki bu tavsiye .clone()ediliyordu, ki bu doğru kod değil) bu bağlamda kullanmak). Ne yazık ki bu soru o kadar çok revizyondan geçti ki orijinal tartışma artık belli değil! Lütfen Corban'ın tavsiyelerine uyun ve bir döngü yazın veya hızı önemsiyorsanız özellikleri doğrudan yeni bir nesneye kopyalayın. Ya da kendiniz test edin!
John Resig

9
Bu bir JavaScript sorusudur (jQuery'den bahsedilmemektedir).
gphilip

60
JQuery kullanmadan bunu nasıl yapabilirim?
Awesomeness01

2264

Bu kritere göz atın : http://jsben.ch/#/bWfk9

Hızın ana endişe olduğu önceki testlerimde buldum

JSON.parse(JSON.stringify(obj))

Bir nesne (daha yavaş derin klonu yavaş şekilde olması jQuery.extend ile deepbayrak% 10-20 doğru ayarlanır).

deepBayrak false(sığ klon) olarak ayarlandığında jQuery.extend oldukça hızlıdır . İyi bir seçenektir, çünkü tür doğrulama için bazı ekstra mantık içerir ve tanımlanmamış özellikler vb. Üzerine kopyalamaz, ancak bu sizi biraz yavaşlatır.

Klonlamaya çalıştığınız nesnelerin yapısını biliyorsanız veya derin iç içe dizilerden kaçınabiliyorsanız, for (var i in obj)hasOwnProperty'yi kontrol ederken nesnenizi klonlamak için basit bir döngü yazabilirsiniz ve jQuery'den çok daha hızlı olacaktır.

Son olarak, bilinen bir nesne yapısını sıcak bir döngüde klonlamaya çalışıyorsanız, sadece klon prosedürünü astarlayarak ve nesneyi manuel olarak oluşturarak ÇOK DAHA FAZLA PERFORMANS elde edebilirsiniz.

JavaScript izleme motorları for..indöngüleri optimize etmekte ve hasOwnProperty'yi kontrol etmek de sizi yavaşlatacaktır. Hız mutlak bir zorunluluk olduğunda manuel klonlama.

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

Kullanarak sakının JSON.parse(JSON.stringify(obj))üzerinde yöntemini Datenesneleri - JSON.stringify(new Date())ISO biçiminde tarih, bir dize temsilini döndürür JSON.parse() vermez bir geri dönüştürme Datenesne. Daha fazla ayrıntı için bu cevaba bakınız .

Ayrıca, en azından Chrome 65'te yerel klonlamanın yolun olmadığını lütfen unutmayın. JSPerf göre, yeni bir fonksiyon oluşturarak yerli klonlama gerçekleştiren neredeyse olduğunu 800x yavaş inanılmaz derecede hızlı bütün yönüyle yoludur JSON.stringify kullanmaktan daha.

ES6 Güncellemesi

Javascript ES6 kullanıyorsanız klonlama veya sığ kopyalama için bu yerel yöntemi deneyin.

Object.assign({}, obj);

4
@trysis Object.create nesneyi klonlamıyor
rahpuser

105
Bu yöntem, değerleri desteklemediği keysiçin object, sizden olanları da kaldırır functions, çünkü JSONişlevleri desteklemez.
Karlen Kishmiryan

39
JSON.parse(JSON.stringify(obj))Tarih Nesneleri üzerinde kullanmanın , tarihi ISO8601 biçimindeki dize temsilinde tarihi UTC'ye dönüştüreceğini de unutmayın .
dnlgmzddr

31
JSON yaklaşımı da dairesel referansları boğmaktadır.
zengin hatırlatıcı

28
@velop, Object.assign ({}, objToClone) sığ bir klon yapıyormuş gibi görünüyor - dev araçlar konsolunda oynarken bunu kullanan nesne klonu hala klonlanmış nesnenin bir referansına işaret etti. Bu yüzden burada gerçekten geçerli olduğunu sanmıyorum.
Garrett Simpson

473

Nesnenizde yalnızca değişkenlerin olduğu ve herhangi bir işlevin olmadığı varsayılarak, şunları kullanabilirsiniz:

var newObject = JSON.parse(JSON.stringify(oldObject));

86
az önce bulduğum gibi bu yaklaşımın con nesneniz herhangi bir işlevi (benim iç alıcılar ve ayarlayıcılar varsa) o zaman bu
stringize

31
@Jason, Bu yöntemin sığ kopyadan (derin bir nesnede) daha yavaş olmasının nedeni, bu yöntemin tanım gereği derin kopyalar olmasıdır. Ancak JSONyerel kodda (çoğu tarayıcıda) uygulandığından, bu, diğer javascript tabanlı derin kopyalama çözümlerinden çok daha hızlı olacaktır ve bazen javascript tabanlı sığ kopyalama tekniğinden daha hızlı olabilir (bkz: jsperf.com/cloning -bir nesne / 79 ).
MiJyn

35
JSON.stringify({key: undefined}) //=> "{}"
Web_Designer

32
bu teknik, Datenesnenin içinde saklanan tüm nesneleri de yok edecek ve bunları dize formuna dönüştürecektir.
fstab

13
Bu JSON spesifikasyonu (parçası olmayan bir şey kopyalamak için başarısız olacak json.org )
cdmckay

397

Yapısal Klonlama

HTML standardı, nesnelerin derin klonlarını oluşturabilen dahili yapılandırılmış bir klonlama / serileştirme algoritması içerir. Hala bazı yerleşik türlerle sınırlıdır, ancak JSON tarafından desteklenen birkaç türe ek olarak Tarihler, RegExps, Haritalar, Kümeler, Bloblar, Dosya Listeleri, ImageDatas, seyrek Diziler, Yazılı Diziler ve muhtemelen gelecekte daha fazlasını destekler . Ayrıca, klonlanmış veriler içindeki referansları koruyarak JSON için hatalara neden olacak döngüsel ve özyinelemeli yapıları desteklemesini sağlar.

Node.js'de Destek: Deneysel 🙂

v8(Düğüm 11 itibariyle) şu anda node.js modül doğrudan yapılandırılmış seri API ortaya çıkarır , ancak bu işlev hala "deneysel" ve gelecekteki sürümlerinde değişiklik veya kaldırma tabi olarak işaretlenir. Uyumlu bir sürüm kullanıyorsanız, bir nesneyi klonlamak şu kadar basittir:

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};

Tarayıcılarda Doğrudan Destek: Belki Sonunda? 😐

Tarayıcılar şu anda yapılandırılmış klonlama algoritması için doğrudan bir arayüz sağlamamaktadır, ancak küresel bir structuredClone()işlev GitHub'daki whatwg / html # 793'te tartışılmıştır . Şu anda önerildiği gibi, çoğu amaç için kullanmak şu kadar basit olacaktır:

const clone = structuredClone(original);

Bu gönderilmediği sürece, tarayıcıların yapılandırılmış klon uygulamaları yalnızca dolaylı olarak gösterilir.

Eşzamansız Geçici Çözüm: Kullanılabilir. 😕

Mevcut API'lerle yapılandırılmış bir klon oluşturmanın en düşük genel yolu, verileri bir MessageChannels'ın bir bağlantı noktasından yayınlamaktır . Diğer port message, ekteki yapısal bir klonla bir olay yayar .data. Ne yazık ki, bu olayları dinlemek mutlaka eşzamansızdır ve eşzamanlı alternatifler daha az pratiktir.

class StructuredCloner {
  constructor() {
    this.pendingClones_ = new Map();
    this.nextKey_ = 0;

    const channel = new MessageChannel();
    this.inPort_ = channel.port1;
    this.outPort_ = channel.port2;

    this.outPort_.onmessage = ({data: {key, value}}) => {
      const resolve = this.pendingClones_.get(key);
      resolve(value);
      this.pendingClones_.delete(key);
    };
    this.outPort_.start();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);

Örnek Kullanım:

const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

main();

Senkron Geçici Çözümler: Korkunç! 🤢

Eşzamanlı olarak yapılandırılmış klonlar oluşturmak için iyi bir seçenek yoktur. İşte bunun yerine birkaç pratik hack.

history.pushState()ve history.replaceState()her ikisi de ilk argümanlarının yapılandırılmış bir klonunu oluşturur ve bu değeri atar history.state. Bunu, bunun gibi herhangi bir nesnenin yapılandırılmış bir klonunu oluşturmak için kullanabilirsiniz:

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

Örnek Kullanım:

Senkron olmasına rağmen, bu son derece yavaş olabilir. Tarayıcı geçmişini değiştirmekle ilişkili tüm ek yüke neden olur. Bu yöntemin tekrar tekrar çağrılması, Chrome'un geçici olarak yanıt vermemesine neden olabilir.

Notificationinşaatçı ilişkili verilerin bir yapılandırılmış klon yaratır. Ayrıca kullanıcıya bir tarayıcı bildirimi görüntülemeye çalışır, ancak bildirim izni istemediğiniz sürece bu sessizce başarısız olur. Başka amaçlarla izniniz olması durumunda, oluşturduğumuz bildirimi hemen kapatırız.

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.onshow = n.close.bind(n);
  return n.data;
};

Örnek Kullanım:


3
@rynah Spesifikasyona tekrar baktım ve haklısın: history.pushState()ve history.replaceState()yöntemleri hem eşzamanlı olarak history.stateilk argümanlarının yapılandırılmış bir klonuna ayarlandı . Biraz garip, ama işe yarıyor. Cevabımı şimdi güncelliyorum.
Jeremy Banks

40
Bu çok yanlış! Bu API'nın bu şekilde kullanılması amaçlanmamıştır.
Fardin K.

209
Firefox'ta pushState'i uygulayan adam olarak, bu hack'te tuhaf bir gurur ve tiksinti karışımı hissediyorum. Aferin çocuklar.
Justin L.

pushState veya Bildirim kesmek Fonksiyonu gibi bazı nesne türleri için çalışmıyor
Shishir Arora

323

Eğer yerleşik bir tane yoksa, deneyebilirsiniz:

function clone(obj) {
    if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

    if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
    else
        var temp = obj.constructor();

    for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            obj['isActiveClone'] = null;
            temp[key] = clone(obj[key]);
            delete obj['isActiveClone'];
        }
    }
    return temp;
}

20
JQuery çözümü sadece herhangi bir Object için değil DOM elemanları için çalışacaktır. Mootooller aynı sınıra sahiptir. Sadece herhangi bir nesne için genel bir "klon" olsaydı ... Özyinelemeli çözüm her şey için işe yarayacaktır. Muhtemelen gitmenin yolu budur.
jschrab

5
Klonlanan nesnede parametreler gerektiren bir yapıcı varsa bu işlev kesilir. Görünüşe göre bunu "var temp = new Object ()" olarak değiştirebiliriz ve her durumda çalışmasını sağlayabiliriz, değil mi?
Andrew Arnott

3
Andrew, bunu var temp = new Object () olarak değiştirirseniz, klonunuz orijinal nesne ile aynı prototipe sahip olmaz. Kullanmayı deneyin: 'var newProto = function () {}; newProto.prototype = obj.constructor; var temp = new newProto (); '
limscoder

1
Limscoder'ın cevabına benzer şekilde, yapıcıyı çağırmadan bunun nasıl yapılacağıyla ilgili aşağıdaki cevabıma bakın: stackoverflow.com/a/13333781/560114
Matt Browne

3
Alt parçalara (yani, nesne ağlarına) başvuru içeren nesneler için bu çalışmaz: İki referans aynı alt nesneye işaret ediyorsa, kopya iki farklı kopyası içerir. Ve özyinelemeli referanslar varsa, işlev asla sonlandırılmaz (en azından istediğiniz şekilde değil :-) Bu genel durumlar için, zaten kopyalanmış nesnelerin sözlüğünü eklemeniz ve zaten kopyalayıp kopyalamadığınızı kontrol etmeniz gerekir. ... Basit bir dil kullandığınızda programlama karmaşıktır
virtualnobi

153

Bir kod satırında bir nesneyi klonlamanın (derin klonlama) etkili yolu

Bir Object.assignyöntem ECMAScript 2015 (ES6) standardının bir parçasıdır ve tam olarak ihtiyacınız olanı yapar.

var clone = Object.assign({}, obj);

Object.assign () yöntemi, numaralandırılabilir tüm özelliklerin değerlerini bir veya daha fazla kaynak nesneden bir hedef nesneye kopyalamak için kullanılır.

Daha fazla oku...

Polyfill eski tarayıcıları desteklemek için:

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}

82
Bu özyinelemeli olarak kopyalanmaz, bu nedenle bir nesneyi klonlama sorununa gerçekten bir çözüm sunmaz.
mwhite

5
Bu yöntem işe yaradı, ancak birkaçını test ettim ve _.extend ({}, (obj)) en hızlı: JSON.parse'dan 20 kat daha hızlı ve Object.assign'dan% 60 daha hızlıydı. Tüm alt nesneleri oldukça iyi kopyalar.
Nico

11
mwhite, klon ve derin klon arasında bir fark vardır. Bu cevap aslında klonlamaktadır, ancak derin klonlamamaktadır.
Meirion Hughes

57
op derin klon istedi. bu derin klon yapmaz.
user566245

9
Bu yöntem yapmak SHALLOW kopyasını değil, bir DERİN kopya ! Bu yüzden cevap tamamen yanlış !
Bharata

97

Kod:

// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
    if (from == null || typeof from != "object") return from;
    if (from.constructor != Object && from.constructor != Array) return from;
    if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
        from.constructor == String || from.constructor == Number || from.constructor == Boolean)
        return new from.constructor(from);

    to = to || new from.constructor();

    for (var name in from)
    {
        to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
    }

    return to;
}

Ölçek:

var obj =
{
    date: new Date(),
    func: function(q) { return 1 + q; },
    num: 123,
    text: "asdasd",
    array: [1, "asd"],
    regex: new RegExp(/aaa/i),
    subobj:
    {
        num: 234,
        text: "asdsaD"
    }
}

var clone = extend(obj);

3
ne var obj = {}veobj.a = obj
neaumusic

5
Bu işlevi anlamıyorum. Varsayalım from.constructorolan Date, örneğin. if2. iftest başarılı olduğunda ve fonksiyonun geri dönmesine neden olduğunda (o zamandan Date != Object && Date != Array) üçüncü teste nasıl ulaşılır ?
Adam McKee

1
@AdamMcKee Çünkü javascript argümanı geçen ve değişken atama zor . Bu yaklaşım, tarihler de dahil olmak üzere harika çalışıyor (gerçekten ikinci test tarafından işleniyor) - burada test etmek için keman yapın: jsfiddle.net/zqv9q9c6 .
brichins

1
@NickSweeting: Deneyin - çalışıyor olabilir. Değilse - düzeltin ve cevabı güncelleyin. Burada toplulukta böyle çalışır :)
Kamarey

1
Bu işlev, testteki normal ifadeyi klonlamaz; "from.constructor! = Object && from.constructor! = Array" koşulu, Number, Date gibi diğer kurucular için her zaman true değerini döndürür.
aMarCruz

95

Ben ne kullanıyorum:

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(typeof(obj[i])=="object" && obj[i] != null)
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

8
Bu doğru görünmüyor. cloneObject({ name: null })=>{"name":{}}
Niyaz

13
Bu, javascript'teki başka bir aptal şeyden kaynaklanıyor, typeof null > "object"ancak Object.keys(null) > TypeError: Requested keys of a value that is not an object.if(typeof(obj[i])=="object" && obj[i]!=null)
Vitim.us

Bu enumerable özelliklerini miras atayabilirsiniz olacak obj doğrudan klon için ve varsayar obj düz nesne olduğunu.
RobG

Bu, sayısal tuşlarla nesnelere dönüştürülen dizileri de karıştırır.
bıçak

Null kullanmıyorsanız sorun değil.
Jorge Bucaran

78

Performansa göre derin kopya: En iyiden en kötüye

  • Yeniden atama "=" (dize dizileri, sayı dizileri - yalnızca)
  • Dilim (dize dizileri, sayı dizileri - yalnızca)
  • Birleştirme (dize dizileri, sayı dizileri - yalnızca)
  • Özel işlev: döngü veya özyinelemeli kopya
  • jQuery's $ .extend
  • JSON.parse (dize dizileri, sayı dizileri, nesne dizileri - yalnızca)
  • Underscore.js 's _.clone (dize dizileri, yalnızca sayı dizileri)
  • Lo-Dash kullanıcısının _.cloneDeep

Bir dizi veya sayı dizisini (bir seviye - referans işaretçisi olmadan) derin kopyalayın:

Bir dizi sayılar ve dizeler içerdiğinde - .slice (), .concat (), .splice () gibi işlevler, atama operatörü "=" ve Underscore.js klon işlevi; dizi öğelerinin derin bir kopyasını oluşturur.

Yeniden atamanın en hızlı performansa sahip olduğu yerlerde:

var arr1 = ['a', 'b', 'c'];
var arr2 = arr1;
arr1 = ['a', 'b', 'c'];

Ve .slice (), .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3 ' den daha iyi bir performansa sahiptir.

var arr1 = ['a', 'b', 'c'];  // Becomes arr1 = ['a', 'b', 'c']
var arr2a = arr1.slice(0);   // Becomes arr2a = ['a', 'b', 'c'] - deep copy
var arr2b = arr1.concat();   // Becomes arr2b = ['a', 'b', 'c'] - deep copy

Bir nesne dizisini (iki veya daha fazla düzey - referans işaretçileri) derin kopyalayın:

var arr1 = [{object:'a'}, {object:'b'}];

Özel bir işlev yazın ($ .extend () veya JSON.parse'dan daha hızlı performansa sahiptir):

function copy(o) {
   var out, v, key;
   out = Array.isArray(o) ? [] : {};
   for (key in o) {
       v = o[key];
       out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
   }
   return out;
}

copy(arr1);

Üçüncü taraf yardımcı program işlevlerini kullanın:

$.extend(true, [], arr1); // Jquery Extend
JSON.parse(arr1);
_.cloneDeep(arr1); // Lo-dash

JQuery'nin $ .extend'inin performansı daha iyi olduğunda:


Birkaçını test ettim ve _.extend ({}, (obj)) en hızlı: JSON.parse'dan 20 kat daha hızlı ve Object.assign'dan% 60 daha hızlıydı. Tüm alt nesneleri oldukça iyi kopyalar.
Nico

4
Tüm örnekleriniz sığ, bir seviye. Bu iyi bir cevap değil. Soru derin klonlama ile ilgilidir, yani en az iki seviye.
Karl Morrison

1
Derin bir kopya, bir nesnenin diğer nesnelere referans işaretçileri kullanılmadan bütünüyle kopyalanmasıdır. JQuery.extend () ve özel işlev (özyinelemeli olan) gibi "Bir nesne dizisini derin kopyala" bölümü altındaki teknikler, nesneleri en az iki düzeyli kopyalar. Yani, tüm örnekler "tek seviyeli" kopyalar değildir.
tfmontague

1
Özel kopyalama işlevinizi beğendim, ancak null değerleri hariç tutmalısınız, aksi takdirde tüm null değerler nesnelere dönüştürülür, yani:out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
josi

2
@HossamMourad - Hata 1 Şubat'ta Josi tarafından düzeltildi (yukarıdaki yorumda) ve yanıtı doğru bir şekilde güncelleyemedim. Maalesef bu hatanın kod tabanınızı yeniden düzenledi.
tfmontague

64
var clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (var i in this) {
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        }
        else
        {
            newObj[i] = this[i];
        }
    }
    return newObj;
}; 

Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});

Güzel cevap ama bu dairesel referanslar için başarısız oluyor.
Luke

59

JavaScript'te derin kopyalama nesneleri (En iyi ve en basit olanı düşünüyorum)

1. JSON.parse kullanma (JSON.stringify (nesne));

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

2. oluşturulan yöntemi kullanarak

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(obj[i] != null &&  typeof(obj[i])=="object")
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = cloneObject(obj);
obj.b.c = 20;

console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

3. Lo-Dash'in _.cloneDeep bağlantı lodash'ını kullanma

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

4. Object.assign () yöntemini kullanma

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

ANCAK NE ZAMAN YANLIŞ

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.

Underscore.js kullanma _.clone link Underscore.js

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

ANCAK NE ZAMAN YANLIŞ

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)

JSBEN.CH Performans Kıyaslama Oyun Alanı 1 ~ 3 http://jsben.ch/KVQLd Performans JavaScript'te derin kopyalama nesneleri


5
Object.assign()derin bir kopyasını yapmaz
Roymunson

1
bunlar için kıstaslar eklemelisiniz; çok yararlı olurdu
jcollum

i üzerinde pop () veya splice () kullanamadım bir dizi içeren bir nesne üzerinde "oluşturulan yöntem" kullandığınızda, neden anlamıyorum? let data = {title:["one", "two"]}; let tmp = cloneObject(data); tmp.title.pop();atmak: TypeError: tmp.title.pop is not a function(tabii ki pop () sadece i eğer iyi çalışır do let tmp = data; ama sonra veri etkilemeden tmp değiştiremiyorum)
hugogogo

Hey, son örneğin yanlış. Bence yanlış örnek için _cloneDeep değil _clone kullanmalısınız.
kenanyildiz

Bu oluşturulan yöntem (2.) diziler için çalışmaz, değil mi?
Toivo Säwén

57

Bunu oldukça iyi yapan bir kütüphane (“klon” olarak adlandırılır) var . Bildiğim gelişigüzel nesnelerin en eksiksiz özyineli klonlama / kopyalamasını sağlar. Ayrıca, henüz diğer cevapların kapsamadığı dairesel referansları da desteklemektedir.

Bunu npm'de de bulabilirsiniz . Tarayıcı ve Node.js için kullanılabilir.

İşte nasıl kullanılacağına dair bir örnek:

İle yükleyin

npm install clone

veya Ender ile paketleyin .

ender build clone [...]

Kaynak kodu manuel olarak da indirebilirsiniz.

Sonra kaynak kodunuzda kullanabilirsiniz.

var clone = require('clone');

var a = { foo: { bar: 'baz' } };  // inital value of a
var b = clone(a);                 // clone a -> b
a.foo.bar = 'foo';                // change a

console.log(a);                   // { foo: { bar: 'foo' } }
console.log(b);                   // { foo: { bar: 'baz' } }

(Feragatname: Kütüphanenin yazarıyım.)


3
npm klonu keyfi olarak yuvalanmış nesneleri klonlamak için benim için çok değerli. Bu doğru cevap.
Andy Ray

diyelim ki lib'inizin performansı JSON.parse(JSON.stringify(obj))nedir?
16'da pkyeck

İşte daha hızlı seçenekler olduğunu belirten bir kütüphane . Yine de test etmedim.
pvorb

İyi çözüm ve bu dairesel referansları destekler (JSON ayrıştırmasının aksine)
Luke

55

Cloning bir Nesne JS'de her zaman bir endişe kaynağıydı, ancak her şey ES6'dan önce, aşağıdaki JavaScript'te bir nesneyi kopyalamanın farklı yollarını listeliyorum, aşağıdaki Nesneye sahip olduğunuzu hayal edin ve bunun derin bir kopyasını almak istiyorum:

var obj = {a:1, b:2, c:3, d:4};

Kökeni değiştirmeden bu nesneyi kopyalamanın birkaç yolu vardır:

1) ES5 +, sizin için kopya yapmak için basit bir fonksiyon kullanarak:

function deepCopyObj(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
        }
        return copy;
    }
    throw new Error("Unable to copy obj this object.");
}

2) ES5 +, JSON.parse ve JSON.stringify kullanarak.

var  deepCopyObj = JSON.parse(JSON.stringify(obj));

3) Açısal J'ler:

var  deepCopyObj = angular.copy(obj);

4) jQuery:

var deepCopyObj = jQuery.extend(true, {}, obj);

5) Alt çizgi js ve yükleme:

var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy

Umarım bu yardım ...


2
alt çizgi klon geçerli sürümünde derin bir klon değil
Rogelio

Teşekkürler. evet Alt Çizgi için yeni belge olarak ... clone_.clone (object) Sağlanan düz nesnenin sığ kopyalanmış bir kopyasını oluşturun. Yuvalanmış nesneler veya diziler kopyalanmadan referans olarak kopyalanacaktır. _.clone ({ad: 'moe'}); => {ad: 'moe'};
Alireza

59
Object.assignyok değil derin kopyalayın. Örnek: var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d". Bu derin bir kopya y.a.bolsaydı, yine de olurdu c, ama şimdi d.
kba

8
Object.assign () yalnızca özelliklerin ilk düzeyini klonlar!
haemse

5
cloneSO () işlevi nedir?
pastorello

53

Bunun eski bir yazı olduğunu biliyorum, ama bunun tökezleyen bir sonraki kişiye biraz yardımcı olabileceğini düşündüm.

Herhangi bir şeye nesne atamadığınız sürece, hafızada hiçbir referansı yoktur. Bu nedenle, diğer nesneler arasında paylaşmak istediğiniz bir nesne yapmak için şöyle bir fabrika oluşturmanız gerekir:

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);

16
Bu cevap gerçekten ilgili değildir çünkü soru şudur: örnek b verilen kişi nasıl kopya oluşturur c Fabrika a'yı bilmiyorken veya fabrika a kullanmak istemezken. Fabrikayı kullanmak istememenin nedeni, b örneklemesinden sonra ek verilerle (örn. Kullanıcı girişi) başlatılmış olabilir.
Noel Abrahams

12
Bunun gerçekten soruya bir cevap olmadığı doğrudur, ancak bence burada olması önemlidir çünkü buraya gelen insanların çoğunun gerçekten sormanın anlamı olduğundan şüphelendiğim sorunun cevabıdır.
Noktalı virgül

8
Üzgünüm çocuklar, neden bu kadar çok oy kullandığını gerçekten anlamıyorum. Bir nesneyi klonlamak oldukça açık bir kavramdır, BAŞKA bir nesneden bir nesneyi konersiniz ve fabrika deseniyle yeni bir nesne oluşturmakla ilgisi yoktur.
Açılış

2
Bu önceden tanımlanmış nesneler için geçerli olsa da, bu şekilde "klonlama" orijinal nesneye eklenen yeni özellikleri tanımaz. A oluşturursanız, a öğesine yeni bir özellik ekleyin, ardından b öğesini oluşturun. b yeni mülke sahip olmayacaktır. Esasen fabrika yapısı yeni özelliklerle değişmez. Bu paradigmatik olarak klonlama değildir. Bkz: jsfiddle.net/jzumbrun/42xejnbx
Jon

1
Bunun iyi bir tavsiye olduğunu düşünüyorum, çünkü kullanmak yerine const defaultFoo = { a: { b: 123 } };gidebilirsiniz const defaultFoo = () => ({ a: { b: 123 } };ve sorununuz çözüldü. Ancak, bu gerçekten sorunun cevabı değildir. Tam bir cevap değil, soruya yorum yapmak daha anlamlı olabilirdi.
Qaribou konumundan Josh

48

Kullanıyorsanız, Underscore.js kitaplığının bir klon yöntemi vardır.

var newObject = _.clone(oldObject);

24
lodash'ın bir cloneDeep yöntemi vardır, ayrıca derinleştirmek için klonlamak için başka bir parametrenin desteklenmesi: lodash.com/docs#clone ve lodash.com/docs#cloneDeep
17

12
@opensas kabul etti. Lodash genellikle alt çizgiden üstündür
nha

7
Bunu ve bir yardımcı program kitaplığının .clone(...)yöntemine tek satırlık başvurular olan diğer tüm yanıtları silmeyi savunuyorum . Her büyük kütüphane bunlara sahip olacak ve tekrarlanan kısa ayrıntılı olmayan cevaplar, o kütüphaneyi kullanmayacak olan çoğu ziyaretçi için yararlı değildir.
Jeremy Banks

41

ConroyP'nin cevabının yukarıdaki, kurucu gerekli parametreleri olsa bile çalışan bir versiyonudur:

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

function deepCopy(obj) {
    if(obj == null || typeof(obj) !== 'object'){
        return obj;
    }
    //make sure the returned object has the same prototype as the original
    var ret = object_create(obj.constructor.prototype);
    for(var key in obj){
        ret[key] = deepCopy(obj[key]);
    }
    return ret;
}

Bu işlev benim simpleoo kütüphanemde de mevcuttur .

Düzenle:

İşte daha sağlam bir sürüm (Justin McCandless sayesinde bu artık döngüsel referansları da destekliyor):

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://stackoverflow.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
    if(src === null || typeof(src) !== 'object'){
        return src;
    }

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Date
    if(src instanceof Date){
        return new Date(src.getTime());
    }
    //RegExp
    if(src instanceof RegExp){
        return new RegExp(src);
    }
    //DOM Element
    if(src.nodeType && typeof src.cloneNode == 'function'){
        return src.cloneNode(true);
    }

    // Initialize the visited objects arrays if needed.
    // This is used to detect cyclic references.
    if (_visited === undefined){
        _visited = [];
        _copiesVisited = [];
    }

    // Check if this object has already been visited
    var i, len = _visited.length;
    for (i = 0; i < len; i++) {
        // If so, get the copy we already made
        if (src === _visited[i]) {
            return _copiesVisited[i];
        }
    }

    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice() by itself would soft clone
        var ret = src.slice();

        //add it to the visited array
        _visited.push(src);
        _copiesVisited.push(ret);

        var i = ret.length;
        while (i--) {
            ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
        }
        return ret;
    }

    //If we've reached here, we have a regular object

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var dest = object_create(proto);

    //add this object to the visited array
    _visited.push(src);
    _copiesVisited.push(dest);

    for (var key in src) {
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        dest[key] = deepCopy(src[key], _visited, _copiesVisited);
    }
    return dest;
}

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

30

Aşağıdakiler aynı nesnenin iki örneğini oluşturur. Ben buldum ve şu anda kullanıyorum. Basit ve kullanımı kolaydır.

var objToCreate = JSON.parse(JSON.stringify(cloneThis));

Bu cevapta yanlış olan bir şey var mı? Bağımsız bir çözüm olarak daha kullanışlı, ancak basit; ancak jQuery çözümü daha popüler. Neden?
ceremcem

Evet, lütfen bana bildirin. Amaçlandığı gibi çalışıyor gibi görünüyor, eğer bir yerde gizli bir kırılma varsa, farklı bir çözüm kullanmam gerekiyor.
nathan rogers

4
Basit bir nesne için, bu Chrome'da verilen cevaba göre yaklaşık 6 kat daha yavaştır ve nesnenin karmaşıklığı arttıkça çok yavaşlar. Çok ölçeklidir ve uygulamanızı çok hızlı bir şekilde azaltabilir.
tic

1
Verilere ihtiyacınız yok, sadece neler olduğunu anlamak. Bu klonlama tekniği, tüm nesneyi bir dizeye serileştirir, ardından bir nesne oluşturmak için bu dize serileştirmesini ayrıştırır. Doğal olarak, bu sadece bazı hafızayı yeniden düzenlemekten çok daha yavaş olacaktır (daha karmaşık klonların yaptığı şey budur). Ancak bu söylenirken, 1000 kat daha az verimli olup olmadığını kimin umursayacağı küçük - orta ölçekli projeler için ("orta ölçekli" tanımınıza bağlı olarak)? Nesneleriniz küçükse ve 1000 tonluk bir tonu klonlamıyorsanız, neredeyse hiçbir şey neredeyse hiçbir şey değildir.
machineghost

3
Ayrıca, bu yöntem yöntemleri (veya JSON'da izin verilmeyen herhangi bir şeyi) kaybeder, artı - JSON.stringify, Date nesnelerini dizelere dönüştürür, ... tersi değil;) Bu çözümden uzak durun.
Bay MT

22

Crockford bu işlevi kullanmanızı önerir (ve ben tercih ederim):

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

var newObject = object(oldObject);

Kısa, beklendiği gibi çalışıyor ve bir kütüphaneye ihtiyacınız yok.


DÜZENLE:

Bu bir çoklu dolgudur Object.create, bu yüzden de kullanabilirsiniz.

var newObject = Object.create(oldObject);

NOT: Bunlardan bazılarını kullanırsanız, kullanan bazı yinelemelerde sorun yaşayabilirsiniz hasOwnProperty. Çünkü, createmiras alan yeni bir boş nesne oluşturun oldObject. Ancak nesneleri klonlamak için hala yararlı ve pratiktir.

Örneğin, oldObject.a = 5;

newObject.a; // is 5

fakat:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false

9
eğer yanılıyorsam beni düzeltin, ama bu Crockford'un prototip kalıtım için başlatma işlevi değil mi? Klon için nasıl uygulanır?
Alex Nolasco

3
Evet, bu tartışmadan korktum: Klon, kopya ve prototip kalıtım arasındaki pratik fark nedir, her birini ne zaman kullanmalısınız ve bu sayfadaki hangi işlevler gerçekten ne yapıyor? Bu SO sayfası "javascript kopya nesnesi" googling bulundu. Gerçekten aradığım şey yukarıdaki işlevdi, bu yüzden tekrar paylaşmak için geldim. Sanırım asker de bunu arıyordu.
Chris Broski

51
Klon / kopya ve kalıtım arasındaki fark şudur, örneğin, bir oldObject özelliğini değiştirdiğimde, özellik newObject'te de değişir. Bir kopya çıkarırsanız, newObject değiştirmeden oldObject ile istediğinizi yapabilirsiniz.
Ridcully

13
Bu hasOwnProperty kontrolünü kıracaktır, böylece bir nesneyi klonlamak için oldukça acayip bir yol ve size beklenmedik sonuçlar verecektir.
Corban Brook

var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; };... davidshariff.com/blog/javascript-inheritance-patterns
Cody

22

Lodash'ın güzel bir _.cloneDeep (değer) yöntemi vardır:

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

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

5
Bunu ve bir yardımcı program kitaplığının .clone(...)yöntemine tek satırlık başvurular olan diğer tüm yanıtları silmeyi savunuyorum . Her büyük kütüphane bunlara sahip olacak ve tekrarlanan kısa ayrıntılı olmayan cevaplar, o kütüphaneyi kullanmayacak olan çoğu ziyaretçi için yararlı değildir.
Jeremy Banks

Daha kolay bir yol kullanmaktır _.merge({}, objA). Sadece lodash nesneleri ilk etapta mutasyona uğratmasaydı, clonefonksiyon gerekli olmazdı.
Rebs

7
Google, JS nesnelerini klonlamak için burayı belirtir. Lodash kullanıyorum, bu yüzden bu cevap benimle alakalı. Lütfen cevapların tüm "wikipedia deletionist" gitmeyelim.
Rebs

2
Düğüm 9'da, JSON.parse (JSON.stringify (arrayOfAbout5KFlatObjects)) _.deepClone'dan (arrayOfAbout5KFlatObjects) çok daha hızlıdır.
Dan Dascalescu

21
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }

17
Nesnenin içinde alt nesneleriniz varsa, başvuruları klonlanacak ve her alt nesnenin değerlerini değil.
Kamarey

1
alt nesneleri derinlemesine klonlanacak şekilde yinelemeli hale getirin.
Ocak'ta fiatjaf

sadece meraklı ... klon değişkeni olmayacak orijinal nesnenin özellikleri işaretçiler olacak? çünkü yeni bellek tahsisi yok gibi görünüyor
Rupesh Patel

3
Evet. Bu sadece sığ bir kopyadır, bu nedenle klon orijinal nesnenin işaret ettiği aynı nesnelere işaret edecektir.
Mark Cidade

Bu bir cevap değil. Kelimenin tam anlamıyla bir nesneyi başka bir nesneye referanslarla dolduruyorsunuz. Kaynak nesnede değişiklik yapılması "klon" da değişiklik yapacaktır.
Shawn Whinnery

19

Sığ kopya tek astarlı ( ECMAScript 5. baskı ):

var origin = { foo : {} };
var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

Ve sığ kopya tek astarlı ( ECMAScript 6. baskı , 2015):

var origin = { foo : {} };
var copy = Object.assign({}, origin);

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

6
Bu basit nesneler için iyi olabilir, ancak yalnızca özellik değerlerini kopyalar. Prototip zincirine dokunmaz ve Object.keysonu kullanarak numaralandırılamayan ve kalıtsal özellikleri atlar. Ayrıca, doğrudan atama yaparak özellik tanımlayıcılarını kaybeder.
Matt Bierner

Prototipi de kopyalarsanız, yalnızca numaralandırılamayan ve özellik tanımlayıcılarını kaçırırsınız, değil mi? Oldukça iyi. :)
sam

Performans bir yana, bu bir nesneyi sığ kopyalamak için gerçekten uygun bir yoldur. Bunu sık sık React bileşenlerimdeki bir yıkıcı atamada sahte dinlenme özellikleri için kullanıyorum.
mjohnsonengr

17

Ben görmedim diye angularjs söz ve bilinmesini ister düşündük ...

angular.copy nesnelerin ve dizilerin derin kopyalanması için bir yöntem de sağlar.


ya da jQiery ile aynı şekilde kullanılabilir:angular.extend({},obj);
Galvani

2
@Galvani: unutulmamalıdır jQuery.extendve angular.extendher ikisi de sığ kopyalarıdır. angular.copyderin bir kopyasıdır.
Dan Atkinson

16

Dizi benzeri nesneler için henüz ideal bir derin klon operatörü yok gibi görünüyor. Aşağıdaki kodun gösterdiği gibi, John Resig'in jQuery klonlayıcı, sayısal olmayan özelliklere sahip dizileri diziler olmayan nesnelere dönüştürür ve RegDwight'ın JSON klonlayıcısı sayısal olmayan özellikleri bırakır. Aşağıdaki testler bu noktaları birden çok tarayıcıda göstermektedir:

function jQueryClone(obj) {
   return jQuery.extend(true, {}, obj)
}

function JSONClone(obj) {
   return JSON.parse(JSON.stringify(obj))
}

var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);

alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
      "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
      "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
      "\nAnd what are the JSONClone names? " + JSONCopy.names)

14
Diğerlerinin Resig'in cevabına yaptığı yorumlarda belirttiği gibi, dizi benzeri bir nesneyi klonlamak istiyorsanız, genişletme çağrısında {} değerini [] olarak değiştirirsiniz, örneğin jQuery.extend (true, [], obj)
Anentropic

15

Amacınız "düz eski bir JavaScript nesnesi" klonlamak veya olmamasına bağlı olarak iki iyi cevap var.

Ayrıca niyetinizin kaynak nesneye prototip referansları olmadan tam bir klon oluşturmak olduğunu varsayalım. Tam bir klonla ilgilenmiyorsanız, diğer bazı cevaplarda (Crockford'un deseni) sağlanan Object.clone () rutinlerinin çoğunu kullanabilirsiniz.

Düz eski JavaScript nesneleri için, modern çalışma zamanlarında bir nesneyi klonlamanın denenmiş ve gerçek bir iyi yolu oldukça basittir:

var clone = JSON.parse(JSON.stringify(obj));

Kaynak nesnenin saf bir JSON nesnesi olması gerektiğini unutmayın. Yani iç içe geçmiş özelliklerinin tümü skaler olmalıdır (boole, dize, dizi, nesne vb.). RegExp veya Date gibi hiçbir işlev veya özel nesne klonlanmayacaktır.

Verimli mi? Heck evet. Her türlü klonlama yöntemini denedik ve bu en iyi sonucu veriyor. Eminim bazı ninja daha hızlı bir yöntem geliştirebilir. Ama sanırım marjinal kazançlardan bahsediyoruz.

Bu yaklaşım basit ve uygulanması kolaydır. Bir kolaylık fonksiyonuna sarın ve gerçekten biraz kazanç elde etmeniz gerekiyorsa, daha sonra gidin.

Artık, düz olmayan JavaScript nesneleri için gerçekten basit bir cevap yok. Aslında, JavaScript işlevlerinin ve iç nesne durumunun dinamik yapısı nedeniyle olamaz. İçindeki işlevlere sahip bir JSON yapısının derin klonlanması, bu işlevleri ve iç bağlamlarını yeniden oluşturmanızı gerektirir. Ve JavaScript'in bunu yapmanın standart bir yolu yoktur.

Bunu yapmanın doğru yolu, kodunuz içinde beyan ettiğiniz ve yeniden kullandığınız bir kolaylık yöntemidir. Kolaylık yöntemi, kendi nesnelerinizin biraz anlaşılmasıyla donatılabilir, böylece grafiği yeni nesne içinde düzgün bir şekilde yeniden oluşturduğunuzdan emin olabilirsiniz.

Kendi yazdık, ancak gördüğüm en iyi genel yaklaşım burada ele alınmaktadır:

http://davidwalsh.name/javascript-clone

Bu doğru fikir. Yazar (David Walsh) genelleştirilmiş işlevlerin klonlanmasını yorumladı. Bu, kullanım durumunuza bağlı olarak yapmayı seçebileceğiniz bir şeydir.

Ana fikir, işlevlerinizin (veya tabiri caizse prototip sınıflarının) somutlaştırılmasını her tür için özel olarak ele almanız gerektiğidir. Burada RegExp ve Date için birkaç örnek sağladı.

Bu kod sadece kısa değil, aynı zamanda çok okunabilir. Uzatmak oldukça kolay.

Bu etkili mi? Heck evet. Hedefin gerçek bir derin kopya klonu ürettiği göz önüne alındığında, kaynak nesne grafiğinin üyelerini yürümek zorunda kalacaksınız. Bu yaklaşımla, tam olarak hangi alt üyelere davranılacağını ve özel türlerin elle nasıl ele alınacağını değiştirebilirsiniz.

Al işte ozaman, buyur. İki yaklaşım. Her ikisi de benim görüşüme göre etkilidir.


13

Bu genellikle en verimli çözüm değildir, ancak ihtiyacım olanı yapar. Aşağıdaki basit test örnekleri ...

function clone(obj, clones) {
    // Makes a deep copy of 'obj'. Handles cyclic structures by
    // tracking cloned obj's in the 'clones' parameter. Functions 
    // are included, but not cloned. Functions members are cloned.
    var new_obj,
        already_cloned,
        t = typeof obj,
        i = 0,
        l,
        pair; 

    clones = clones || [];

    if (obj === null) {
        return obj;
    }

    if (t === "object" || t === "function") {

        // check to see if we've already cloned obj
        for (i = 0, l = clones.length; i < l; i++) {
            pair = clones[i];
            if (pair[0] === obj) {
                already_cloned = pair[1];
                break;
            }
        }

        if (already_cloned) {
            return already_cloned; 
        } else {
            if (t === "object") { // create new object
                new_obj = new obj.constructor();
            } else { // Just use functions as is
                new_obj = obj;
            }

            clones.push([obj, new_obj]); // keep track of objects we've cloned

            for (key in obj) { // clone object members
                if (obj.hasOwnProperty(key)) {
                    new_obj[key] = clone(obj[key], clones);
                }
            }
        }
    }
    return new_obj || obj;
}

Devirli dizi testi ...

a = []
a.push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true

Fonksiyon testi...

f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false

11

angularjs

Eğer açısal kullanıyorsanız bunu da yapabilirsiniz

var newObject = angular.copy(oldObject);

11

Buradaki en büyük oylarla cevaba katılmıyorum . Bir özyinelemeli Derin Clone olduğunu çok daha hızlı daha JSON.parse (JSON.stringify (obj)) söz konusu yaklaşım.

Ve işte hızlı başvuru işlevi:

function cloneDeep (o) {
  let newO
  let i

  if (typeof o !== 'object') return o

  if (!o) return o

  if (Object.prototype.toString.apply(o) === '[object Array]') {
    newO = []
    for (i = 0; i < o.length; i += 1) {
      newO[i] = cloneDeep(o[i])
    }
    return newO
  }

  newO = {}
  for (i in o) {
    if (o.hasOwnProperty(i)) {
      newO[i] = cloneDeep(o[i])
    }
  }
  return newO
}

2
Bu yaklaşımı beğendim ama tarihleri ​​düzgün işlemiyor; if(o instanceof Date) return new Date(o.valueOf());null değerini kontrol ettikten sonra gibi bir şey eklemeyi düşünün
Luis

Dairesel referanslarda çökmeler.
Harry

En son kararlı Firefox'ta, bu Jsben.ch bağlantısındaki diğer stratejilerden çok daha büyük veya daha büyük bir sıraya göre daha uzundur. Diğerlerini yanlış yönde atıyor.
WBT

11
// obj target object, vals source object
var setVals = function (obj, vals) {
    if (obj && vals) {
        for (var x in vals) {
            if (vals.hasOwnProperty(x)) {
                if (obj[x] && typeof vals[x] === 'object') {
                    obj[x] = setVals(obj[x], vals[x]);
                } else {
                    obj[x] = vals[x];
                }
            }
        }
    }
    return obj;
};

10

Sadece ECMAScript 6 veya transpilleri kullanabiliyorsanız .

Özellikleri:

  • Kopyalama sırasında alıcı / ayarlayıcıyı tetiklemez.
  • Alıcı / ayarlayıcıyı korur.
  • Prototip bilgilerini korur.
  • Her iki çalışır nesne değişmezi ve fonksiyonel OO yazı stilleri.

Kod:

function clone(target, source){

    for(let key in source){

        // Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter.
        let descriptor = Object.getOwnPropertyDescriptor(source, key);
        if(descriptor.value instanceof String){
            target[key] = new String(descriptor.value);
        }
        else if(descriptor.value instanceof Array){
            target[key] = clone([], descriptor.value);
        }
        else if(descriptor.value instanceof Object){
            let prototype = Reflect.getPrototypeOf(descriptor.value);
            let cloneObject = clone({}, descriptor.value);
            Reflect.setPrototypeOf(cloneObject, prototype);
            target[key] = cloneObject;
        }
        else {
            Object.defineProperty(target, key, descriptor);
        }
    }
    let prototype = Reflect.getPrototypeOf(source);
    Reflect.setPrototypeOf(target, prototype);
    return target;
}

9

İşte herhangi bir JavaScript nesnesini klonlayabilen kapsamlı bir clone () yöntemi. Hemen hemen tüm vakaları ele alır:

function clone(src, deep) {

    var toString = Object.prototype.toString;
    if (!src && typeof src != "object") {
        // Any non-object (Boolean, String, Number), null, undefined, NaN
        return src;
    }

    // Honor native/custom clone methods
    if (src.clone && toString.call(src.clone) == "[object Function]") {
        return src.clone(deep);
    }

    // DOM elements
    if (src.nodeType && toString.call(src.cloneNode) == "[object Function]") {
        return src.cloneNode(deep);
    }

    // Date
    if (toString.call(src) == "[object Date]") {
        return new Date(src.getTime());
    }

    // RegExp
    if (toString.call(src) == "[object RegExp]") {
        return new RegExp(src);
    }

    // Function
    if (toString.call(src) == "[object Function]") {

        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });
    }

    var ret, index;
    //Array
    if (toString.call(src) == "[object Array]") {
        //[].slice(0) would soft clone
        ret = src.slice();
        if (deep) {
            index = ret.length;
            while (index--) {
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }
    return ret;
};

İlkel maddeleri sarıcı nesnelere dönüştürür, çoğu durumda iyi bir çözüm değildir.
Danubian Sailor

@DanubianSailor - Öyle sanmıyorum ... ilkelleri en başından döndürüyor gibi görünüyor ve onlara geri döndüklerinde onları sarıcı nesnelere dönüştürecek bir şey yapmıyor gibi görünüyor.
Jimbo Jonny
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.