Bir JavaScript nesnesini nasıl doğru şekilde klonlayabilirim?


3078

Bir nesnem var x. Ben nesne olarak kopyalamak istediğiniz ydeğişiklikler için böyle, ydeğiştirmeyin x. Yerleşik JavaScript nesnelerinden türetilen nesnelerin kopyalanmasının ekstra, istenmeyen özelliklerle sonuçlanacağını fark ettim. Bu sorun değil, çünkü kendi hazırladığım nesnelerden birini kopyalıyorum.

Bir JavaScript nesnesini nasıl doğru şekilde klonlayabilirim?



256
JSON için kullanıyorummObj=JSON.parse(JSON.stringify(jsonObject));
Lord Loh.

67
Neden kimsenin önermediğini anlamıyorum Object.create(o), yazarın sorduğu her şeyi yapıyor mu?
froginvasion

44
var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2; Bunu yaptıktan sonra, y.deep.key2 de olacak, bu nedenle Object.create klonlama için KULLANILAMAZ ...
Ruben Stolk

17
@ r3wt işe yaramayacak ... Lütfen sadece çözümün temel testini yaptıktan sonra gönderin ..
akshay

Yanıtlar:


1561

JavaScript'teki herhangi bir nesne için bunu yapmak basit veya basit olmayacaktır. Nesnenin prototipinden, prototipte bırakılması ve yeni örneğe kopyalanmaması gereken öznitelikleri yanlışlıkla alma sorunuyla karşılaşacaksınız. Örneğin, bir ekliyorsanız cloneyöntemi için Object.prototypebazı cevaplar tasvir gibi, açıkça o niteliği atlamak gerekir. Ancak Object.prototype, bilmediğiniz başka ek yöntemler veya diğer ara prototipler varsa ne olur? Bu durumda, yapmamanız gereken nitelikleri kopyalayacaksınız, bu nedenle yöntemle öngörülemeyen, yerel olmayan nitelikleri tespit etmeniz gerekir hasOwnProperty.

Numaralandırılamayan özniteliklere ek olarak, gizli özelliklere sahip nesneleri kopyalamaya çalıştığınızda daha zor bir sorunla karşılaşırsınız. Örneğin, prototypebir işlevin gizli bir özelliğidir. Ayrıca, bir nesnenin prototipine __proto__gizli olan özniteliğe başvurulur ve kaynak nesnenin öznitelikleri üzerinde yinelenen bir for / in döngüsü tarafından kopyalanmaz. __proto__Firefox'un JavaScript yorumlayıcısına özgü olabileceğini düşünüyorum ve diğer tarayıcılarda farklı bir şey olabilir, ancak resmi elde edersiniz. Her şey numaralandırılamaz. Gizli bir özelliği adını biliyorsanız kopyalayabilirsiniz, ancak otomatik olarak keşfetmenin hiçbir yolunu bilmiyorum.

Zarif bir çözüm arayışındaki bir başka engel ise prototip mirasını doğru bir şekilde kurma problemidir. Kaynak nesnenizin prototipi ise Object, o zaman yeni bir genel nesne oluşturmak {}işe yarayacaktır, ancak kaynağın prototipi Objecttorunundan biriyse, hasOwnPropertyfiltreyi kullanarak atladığınız veya prototipteydi, ama ilk etapta numaralandırılamadı. Bir çözüm constructor, ilk kopya nesnesini almak ve daha sonra özniteliklerin üzerine kopyalamak için kaynak nesnenin özelliğini çağırmak olabilir , ancak yine de numaralandırılamayan öznitelikler almayacaksınız. Örneğin, bir Datenesne verilerini gizli bir üye olarak depolar:

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

Tarih dizesi, tarihinin d15 saniye gerisindedir d2. Birini Datediğeriyle yapmanın bir yolu , setTimeyöntemi çağırmaktır , ancak bu Datesınıfa özgüdür . Bu soruna kurşun geçirmez bir genel çözüm olduğunu düşünmüyorum, ancak yanlış olmaktan mutluluk duyacağım!

Ben ancak apaçık bir kopyalamanız gerekmektedir ettiği varsayılarak ödün sona erdi kopyalama genel derine uygulamak zorunda kalınca Object, Array, Date, String, Number, veya Boolean. Son 3 tip değişmez, bu yüzden sığ bir kopya yapabilir ve değişmesinden endişe etmeyebilirim. Ayrıca, bu listedeki 6 basit türden birinde bulunan Objectveya Arraybu elemanlardan biri olacağını varsaydım . Bu, aşağıdaki gibi bir kodla gerçekleştirilebilir:

function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

Yukarıdaki işlev, nesneler ve dizilerdeki veriler bir ağaç yapısı oluşturduğu sürece bahsettiğim 6 basit tip için yeterli şekilde çalışacaktır. Yani, nesnede aynı verilere birden fazla başvuru yoktur. Örneğin:

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

Herhangi bir JavaScript nesnesini işleyemez, ancak sadece attığınız herhangi bir şey için çalışacağını varsaymadığınız sürece birçok amaç için yeterli olabilir.


5
neredeyse bir nodejs'da iyi çalıştı - sadece (var i = 0, var len = obj.length; i <len; ++ i) {için (var i = 0; i <obj.length; ++ i) {
Trindaz

5
Gelecekteki googlerslar için: aynı derin kopya, gist.github.com/2234277
Trindaz

7
Günümüzde JSON.parse(JSON.stringify([some object]),[some revirer function])bir çözüm olabilir mi?
KooiInc

5
İlk kod parçasında olmaması gerektiğinden emin misiniz var cpy = new obj.constructor()?
cyon

8
Nesne atama, Chrome'da gerçek bir kopya oluşturmuyor gibi görünüyor, orijinal nesneye referans sağlıyor - JSON.stringify ve JSON.parse'yi klonlamak için sona erdi - mükemmel çalıştı
1owk3y

974

Kullanmak yoksa Dates, işlevler, tanımsız, RegExp veya nesne içinde Infinity, çok basit bir liner ise 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'
}
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()

Bu, nesne, dizi, dize, boole ve sayı içeren her türlü nesne için geçerlidir.

Ayrıca , bir çalışana / çalışandan ileti gönderirken kullanılan tarayıcıların yapılandırılmış klon algoritması hakkındaki bu makaleye de bakın . Ayrıca derin klonlama için bir işlev içerir.


42
Bunun yalnızca test için kullanılabileceğini unutmayın. İlk olarak, zaman ve bellek tüketimi açısından optimal olmaktan çok uzak. İkinci olarak, tüm tarayıcılarda bu yöntemler yoktur.
Nux

2
Her zaman JSON2.js veya JSON3.js dosyasını ekleyebilirsiniz. Yine de uygulamanız için onlara ihtiyacınız olacak. Ancak JSON.stringify kalıtsal özellikleri içermediğinden, bunun en iyi çözüm olmayabileceğini kabul ediyorum.
Tim Hong

78
@Nux, Neden zaman ve bellek açısından optimal değil? MiJyn diyor ki: "Bu yöntemin sığ kopyalamadan (derin bir nesnede) daha yavaş olmasının nedeni, bu yöntemin tanım gereği derin kopyalar olmasıdır. Ancak JSON yerel kodda (çoğu tarayıcıda) uygulandığından, bu çok daha hızlı olacaktır JavaScript tabanlı derin kopyalama çözümlerinden daha hızlı olabilir ve bazen javascript tabanlı sığ kopyalama tekniğinden daha hızlı olabilir (bkz: jsperf.com/cloning-an-object/79). " stackoverflow.com/questions/122102/…
BeauCielBleu

16
Sadece Ekim 2014 için bir güncelleme eklemek istiyorum. Chrome 37+ JSON.parse (JSON.stringify (oldObject)) ile daha hızlıdır; Bunu kullanmanın yararı, bir javascript motorunun istediği takdirde daha iyi bir şey görmesi ve optimize etmesi çok kolay olmasıdır.
mirhagk

17
2016 güncellemesi: Bu, yaygın olarak kullanılan hemen hemen her tarayıcıda çalışmalıdır. (bkz. Kullanabilir miyim ... ) Şimdi asıl soru, yeterli performans gösterip göstermediği olacaktır.
James Foster

783

JQuery ile, sığ kopyayı uzatma ile yapabilirsiniz :

var copiedObject = jQuery.extend({}, originalObject)

daha sonra yapılacak değişiklikler değişiklikleri copiedObjectetkilemez originalObjectve bunun tersi de geçerlidir.

Veya derin bir kopya oluşturmak için :

var copiedObject = jQuery.extend(true, {}, originalObject)

164
hatta:var copiedObject = jQuery.extend({},originalObject);
Grant McLean

82
Ayrıca derin kopya için ilk parametre olarak true belirtmek için yararlı:jQuery.extend(true, {}, originalObject);
Will Shaver

6
Evet, bu bağlantıyı faydalı buldum (Pascal ile aynı çözüm) stackoverflow.com/questions/122102/…
Garry English

3
Sadece bir not, bu orijinal nesnenin proto yapıcısını kopyalamaz
Sam Jones

1
Stackoverflow.com/questions/184710/… 'a göre , "sığ kopya" sadece originalObject referansını kopyalıyor gibi görünüyor, neden burada söylüyor ...subsequent changes to the copiedObject will not affect the originalObject, and vice versa.... Üzgünüm, gerçekten kafam karıştı.
Carr

687

ECMAScript 6'da , numaralandırılabilir tüm özelliklerin değerlerini bir nesneden diğerine kopyalayan Object.assign yöntemi vardır . Örneğin:

var x = {myProp: "value"};
var y = Object.assign({}, x); 

Ancak, iç içe nesnelerin hala referans olarak kopyalandığını unutmayın.


Evet, inanıyorum ki Object.assignbu yol. Çok doldurmak

22
(Bunlar beri Ayrıca, bu nesne değişmezleri aracılığıyla tanımlanan "yöntemleri" üzerine kopyalar unutmayın vardır ama enumerable) değil "sınıfı" mekanizmasıyla meydan yöntemleri (bunlar beri değildir enumerable).
Marcus Junius Brutus

16
Bunun Edge dışında IE tarafından desteklenmediğinden bahsetmeliyim. Bazı insanlar bunu hala kullanıyor.
Saulius

1
Bu cevap @EugeneTiurin ile aynı.
Wilt

5
Burada deneyimden bahsetmişken ... Bunu KULLANMAYIN. Nesneler hiyerarşik olacak şekilde tasarlanmıştır ve yalnızca ilk seviyeyi kopyalarsınız, böylece tüm nesneyi kopyalamanız için atamanız sağlanır. Güven bana, günlerce kafa çizilmesinden kurtarabilir.
ow3n

232

MDN başına :

  • Sığ kopya istiyorsanız, Object.assign({}, a)
  • "Derin" kopya için, JSON.parse(JSON.stringify(a))

Harici kitaplıklara gerek yoktur, ancak önce tarayıcı uyumluluğunu kontrol etmeniz gerekir .


8
JSON.parse (JSON.stringify (a)) güzel görünüyor, ancak kullanmadan önce istediğiniz koleksiyon için gereken süreyi karşılaştırmanızı öneririm. Nesne boyutuna bağlı olarak, bu en hızlı seçenek olmayabilir.
Edza

3
Ben fark JSON yöntemi tarih nesneleri dizelere dönüştürür ama tarihlere geri değil. Javascript'te saat dilimlerinin eğlencesiyle uğraşmak ve herhangi bir tarihi manuel olarak düzeltmek zorundasınız. Tarihleri yanı sıra diğer türleri için benzer davaları olabilir
Steve Seeger

Date için işlevselliği olduğundan moment.js'yi kullanırım clone. burada daha fazlasını görmek momentjs.com/docs/#/parsing/moment-clone
Tareq

Bu iyi ama nesnenin içinde işlevleri varsa dikkat edin
lesolorzanov

Json ayrıştırma ile ilgili bir başka konu da dairesel referanslardır. Nesnenin içinde dairesel referanslar varsa, bu kırılacaktır
philoj

133

Birçok cevap var, ancak hiçbiri ECMAScript 5'ten Object.create'den bahsetmiyor , bu da kuşkusuz size tam bir kopya vermiyor, ancak kaynağı yeni nesnenin prototipi olarak ayarlıyor.

Bu nedenle, bu soruya kesin bir cevap değildir, ancak tek satırlık bir çözümdür ve bu nedenle zariftir. Ve en iyi 2 vaka için çalışır:

  1. Böyle bir mirasın yararlı olduğu yerler (duh!)
  2. Kaynak nesnenin değiştirilmeyeceği yerde, bu nedenle 2 nesne arasındaki ilişki sorun yaratmaz.

Misal:

var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property

Bu çözümü neden daha üstün buluyorum? Yerli, bu yüzden döngü yok, özyineleme yok. Ancak, eski tarayıcıların çoklu doldurmaya ihtiyacı olacaktır.


Not: Object.create derin bir kopya değildir (itpastorn özyinelemeden bahsetmiştir). Kanıt:var a = {b:'hello',c:{d:'world'}}, b = Object.create(a); a == b /* false */; a.c == b.c /* true */;
zamnuts

103
Bu prototip miras, klonlama değil. Bunlar tamamen farklı şeyler. Yeni nesnenin kendi özellikleri yoktur, sadece prototipin özelliklerini gösterir. Klonlamanın amacı, başka bir nesnedeki hiçbir özelliğe başvurmayan yeni ve yeni bir nesne oluşturmaktır.
d13

7
Sana tamamen katılıyorum. Bunun 'amaçlandığı gibi' klonlama olmadığını da kabul ediyorum. Ama hadi insanlar, standart olmayan belirsiz çözümler bulmaya çalışmak yerine JavaScript'in doğasını kucaklayın. Tabii, prototiplerden hoşlanmıyorsunuz ve hepsi sizin için "falan", ama aslında ne yaptığınızı biliyorsanız çok faydalılar.
froginvasion

4
@RobG: Bu makalede, başvuru ve klonlama arasındaki fark açıklanmaktadır: en.wikipedia.org/wiki/Cloning_(programming) . Object.createEbeveynin özelliklerini referanslar aracılığıyla gösterir.Bu, ebeveynin özellik değerleri değişirse çocuğun da değişeceği anlamına gelir. Bunun farkında değilseniz, kodunuzda bulunması zor hatalara neden olabilecek iç içe diziler ve nesnelerle bazı şaşırtıcı yan etkileri vardır: jsbin.com/EKivInO/2 . Klonlanmış bir nesne, üst öğeyle aynı özelliklere ve değerlere sahip olan ancak üst öğeye bağlı olmayan tamamen yeni, bağımsız bir nesnedir.
d13

1
Bu insanları yanlış yönlendirir ... Object.create (), kalıtım için bir araç olarak kullanılabilir, ancak klonlama hiçbir yere yakın değildir.
prajnavantha

128

Bir Javascript nesnesini bir kod satırında klonlamanın zarif bir 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;
    }
  });
}

Aptal soru için özür dilerim, ancak çoklu dolgudaki işlev yalnızca bir parametre aldığında neden Object.assigniki parametre valuealıyor?
Qwertie

@Qwertie dün Tüm argümanlar yinelenir ve tek bir nesnede birleştirilir, son geçirilen argümanın özelliklerine öncelik verilir
Eugene Tiurin

Oh, anlıyorum, teşekkürler (daha argumentsönce nesneyi bilmiyordum.) Object()Google aracılığıyla bulmakta sorun yaşıyorum ... bu bir tahmin, değil mi?
Qwertie

44
Bu sadece sığ bir "klonlama" yapacak
Marcus Junius Brutus

1
Bu cevap şu şekilde aynıdır: stackoverflow.com/questions/122102/… Aynı kişi tarafından olduğunu biliyorum ama sadece cevabı kopyalamak yerine referans vermelisiniz.
lesolorzanov

87

İnternette çoğu çözümle ilgili çeşitli sorunlar var. Bu yüzden, kabul edilen cevabın neden kabul edilmemesi gerektiğini içeren bir takip yapmaya karar verdim.

başlama durumu

Bir Javascript'i tüm çocukları ve çocukları vb. İle birlikte kopyalamak istiyorum Object. Ben tür normal bir geliştiricinin olduğuma Ama benim Objectsahiptir , normal properties , circular structureshatta nested objects.

Şimdi bir circular structureve bir nested objectilk oluşturalım .

function Circ() {
    this.me = this;
}

function Nested(y) {
    this.y = y;
}

Her şeyi bir Objectisimde bir araya getirelim a.

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('a')
};

Daha sonra, aadlı bir değişkene kopyalamak bve değiştirmek istiyoruz .

var b = a;

b.x = 'b';
b.nested.y = 'b';

Burada ne olduğunu biliyorsunuz çünkü eğer olmasaydınız bu büyük soruya bile inmezdiniz.

console.log(a, b);

a --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

Şimdi bir çözüm bulalım.

JSON

Denediğim ilk deneme kullanmaktı JSON.

var b = JSON.parse( JSON.stringify( a ) );

b.x = 'b';
b.nested.y = 'b';

Üzerinde çok fazla zaman kaybetmeyin, elde edersiniz TypeError: Converting circular structure to JSON.

Özyinelemeli kopya (kabul edilen "cevap")

Kabul edilen cevaba bir bakalım.

function cloneSO(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }

    // Handle Object
    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! Its type isn't supported.");
}

İyi görünüyor, değil mi? Nesnenin özyinelemeli bir kopyası ve diğer türleri de ele alıyor Date, ancak bu bir gereklilik değildi.

var b = cloneSO(a);

b.x = 'b';
b.nested.y = 'b';

Özyineleme ve circular structuresbirlikte iyi çalışmıyor ...RangeError: Maximum call stack size exceeded

doğal çözüm

İş arkadaşımla tartıştıktan sonra, patronum bize ne olduğunu sordu ve biraz çalıştıktan sonra basit bir çözüm buldu . Denir Object.create.

var b = Object.create(a);

b.x = 'b';
b.nested.y = 'b';

Bu çözüm bir süre önce Javascript'e eklendi ve hatta kolları circular structure.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

... ve görüyorsunuz, içindeki iç içe geçmiş yapı ile çalışmadı.

doğal çözüm için çoklu dolgu

Object.createEski tarayıcıda tıpkı IE 8 gibi bir çoklu dolgu var. Mozilla tarafından önerilen gibi bir şey ve elbette mükemmel değil ve yerel çözümle aynı soruna neden oluyor .

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

var b = clonePF(a);

b.x = 'b';
b.nested.y = 'b';

Bize anlattıklarına Fbir göz atabilmek için kapsamın dışına çıktım instanceof.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> F {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> true

Yerel çözümle aynı sorun , ancak biraz daha kötü çıktı.

daha iyi (ama mükemmel olmayan) çözüm

Etrafta kazarken, benzer bir soru buldum ( Javascript'te, derin bir kopya yaparken, bir özelliğin "bu" olması nedeniyle bir döngüyü nasıl önleyebilirim? ), Ancak bir şekilde daha iyi bir çözüm.

function cloneDR(o) {
    const gdcc = "__getDeepCircularCopy__";
    if (o !== Object(o)) {
        return o; // primitive value
    }

    var set = gdcc in o,
        cache = o[gdcc],
        result;
    if (set && typeof cache == "function") {
        return cache();
    }
    // else
    o[gdcc] = function() { return result; }; // overwrite
    if (o instanceof Array) {
        result = [];
        for (var i=0; i<o.length; i++) {
            result[i] = cloneDR(o[i]);
        }
    } else {
        result = {};
        for (var prop in o)
            if (prop != gdcc)
                result[prop] = cloneDR(o[prop]);
            else if (set)
                result[prop] = cloneDR(cache);
    }
    if (set) {
        o[gdcc] = cache; // reset
    } else {
        delete o[gdcc]; // unset again
    }
    return result;
}

var b = cloneDR(a);

b.x = 'b';
b.nested.y = 'b';

Çıktıya bir göz atalım ...

console.log(a, b);

a --> Object {
    x: "a",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "a"
    }
}

b --> Object {
    x: "b",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> false

Gereksinim eşleşti, ancak değiştirme dahil bazı küçük sorunlar hala var instanceiçinde nestedve circhiç Object.

Bir yaprağı paylaşan ağaçların yapısı kopyalanmaz, iki bağımsız yaprak haline gelirler:

        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]

Sonuç

Özyineleme ve önbellek kullanan son çözüm en iyi olmayabilir, ancak nesnenin gerçek bir derin kopyasıdır. Çok basit kolları properties, circular structuresve nested object, ama olacak bunların örneğine kadar karışıklık klonlama sırasında.

jsfiddle


12
bu nedenle bu sorunu önlemek :)
mikus

mikus sadece temel kullanım durumlarından fazlasını kapsayan gerçek bir şartname olana kadar , evet.
Fabio Poloni

2
Yukarıda sunulan çözümlerin iyi bir analizi ancak yazar tarafından çıkarılan sonuç, bu sorunun çözümünün olmadığını göstermektedir.
Amir Mog

2
JS'nin doğal klon işlevini içermemesi utanç vericidir.
l00k

1
En iyi cevaplar arasında, bunun doğru olana yakın olduğunu hissediyorum.
KTÜ

77

Sığ bir kopyayla iyiyseniz, underscore.js kütüphanesinde bir klonlama yöntemi vardır.

y = _.clone(x);

ya da şöyle uzatabilirsiniz

copiedObject = _.extend({},originalObject);

2
Teşekkürler. Bu tekniği bir Meteor sunucusunda kullanma.
Turbo

Lodash ile hızlı bir şekilde başlamak için, npm, Browserify ve lodash öğrenmenizi tavsiye ederim. 'Npm i --sada lodash.clone' ile çalışmak için klon aldım ve sonra 'var clone = requir (' lodash.clone ');' Çalışmak için gereksinim duymak için browserify gibi bir şeye ihtiyacınız var. Yükledikten ve nasıl çalıştığını öğrendikten sonra, kodunuzu her çalıştırdığınızda (doğrudan Chrome'a ​​girmek yerine) 'browserify yourfile.js> bundle.js; başlat chrome index.html' seçeneğini kullanırsınız. Bu, dosyanızı ve npm modülünden gerekli olan tüm dosyaları bundle.js'ye toplar. Muhtemelen zamandan tasarruf edebilir ve Gulp ile bu adımı otomatikleştirebilirsiniz.
Aaron Bell

65

Tamam, aşağıda bu nesneye sahip olduğunuzu ve klonlamak istediğinizi düşünün:

let obj = {a:1, b:2, c:3}; //ES6

veya

var obj = {a:1, b:2, c:3}; //ES5

cevap esas olarak hangi ECMAscript'i kullandığınıza bağlıdır, ES6+sadece Object.assignklon yapmak için kullanabilirsiniz :

let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};

veya şu şekilde yayılmış işleci kullanarak:

let cloned = {...obj}; //new {a:1, b:2, c:3};

Ancak, kullanıyorsanız ES5, birkaç yöntem kullanabilirsiniz, ancak, JSON.stringifykopyalamak için büyük bir veri parçası için kullanmadığınızdan emin olun, ancak birçok durumda bu gibi bir satır kullanışlı olabilir:

let cloned = JSON.parse(JSON.stringify(obj)); 
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over

Neye big chunk of dataeşit olacağına örnek verebilir misiniz? 100kb? 100MB? Teşekkürler!
user1063287

Evet, @ user1063287, temelde daha büyük veriler, performans daha kötü ... bu yüzden gerçekten bir kb, mb veya gb'ye bağlı değil, bunu kaç kez yapmak istediğinize daha çok bağlı ... Ayrıca işe yaramayacak fonksiyonlar ve diğer şeyler için ...
Alireza

3
Object.assignsığ bir kopya yapar (tıpkı forma gibi, @Alizera)
Bogdan D

Let in es5: ^) @Alireza
SensationSama

40

Özellikle yetersiz bir çözüm, üye yöntemleri olmayan nesnelerin derin kopyalarını oluşturmak için JSON kodlamasını kullanmaktır. Metodoloji, JSON'u hedef nesnenizi kodlamaktır, daha sonra kodunu çözerek aradığınız kopyayı elde edersiniz. İstediğiniz kadar kopya yapmak istediğiniz kadar kod çözebilirsiniz.

Tabii ki, işlevler JSON'a ait değildir, bu nedenle bu yalnızca üye yöntemleri olmayan nesneler için çalışır.

JSON bloblarını bir anahtar / değer deposunda sakladığım ve bir JavaScript API'sında nesne olarak gösterildiklerinde, her nesne aslında nesnenin orijinal durumunun bir kopyasını içerdiğinden, bu yöntem kullanım durumum için mükemmeldi. arayan kişi maruz kalan nesneyi mutasyona uğrattıktan sonra deltayı hesaplayabilir.

var object1 = {key:"value"};
var object2 = object1;

object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);

object2.key = "a change";
console.log(object1);// returns value

Neden fonksiyonlar JSON'a ait değil? Onları bir kereden fazla JSON olarak transfer ettiklerini gördüm ...
the_drow

5
Fonksiyonlar, veri aktarmanın güvenli (veya akıllı) bir yolu olmadığından JSON spesifikasyonunun bir parçası değildir, bu da JSON için yapılmıştır. Firefox'taki yerel JSON kodlayıcısının kendisine aktarılan işlevleri görmezden geldiğini biliyorum, ancak başkalarının davranışlarından emin değilim.
Kris Walker

1
@mark: { 'foo': function() { return 1; } }gerçek anlamıyla oluşturulmuş bir nesnedir.
abarnert

@abarnert işlevleri veri değildir. "İşlev değişmez değerleri" bir yanlış adlandırmadır - işlevler atamalar ve her türlü "serileştirilemeyen" şeyler de dahil olmak üzere rastgele kod içerebilir.
vemv

35

Referans olmadan bir nesneyi kopyalamak için spread özelliğini kullanabilirsiniz . Ancak dikkatli olun (yorumlara bakın), 'kopya' sadece en düşük nesne / dizi düzeyinde. Yuvalanmış özellikler hala referanstır!


Komple klon:

let x = {a: 'value1'}
let x2 = {...x}

// => mutate without references:

x2.a = 'value2'
console.log(x.a)    // => 'value1'

İkinci düzeyde referanslarla klonlayın:

const y = {a: {b: 'value3'}}
const y2 = {...y}

// => nested object is still a references:

y2.a.b = 'value4'
console.log(y.a.b)    // => 'value4'

JavaScript aslında derin klonları yerel olarak desteklemez. Bir yardımcı program işlevi kullanın. Örneğin Ramda:

http://ramdajs.com/docs/#clone


1
Bu çalışmıyor ... muhtemelen x bir dizi olacağı zaman işe yarayacaktır, örneğin x = ['ab', 'cd', ...]
Kamil Kiełczewski

3
Bu işe yarıyor, ancak bunun bir SHALLOW kopyası olduğunu unutmayın, bu nedenle diğer nesnelere yapılan derin referanslar referans olarak kalır!
Bugs Bunny

Kısmi bir klon da bu şekilde olabilir:const first = {a: 'foo', b: 'bar'}; const second = {...{a} = first}
Cristian Traìna

25

AngularJS kullananlar için, bu kütüphanedeki nesnelerin klonlanması veya genişletilmesi için doğrudan bir yöntem de vardır.

var destination = angular.copy(source);

veya

angular.copy(source, destination);

Daha fazla açısal.copy belgesinde ...


2
Bu derin bir kopya FYI.
zamnuts

22

İşte kullanabileceğiniz bir işlev.

function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;    
    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);    
    return temp;
}

10
Bu cevap oldukça yakın, ama tam olarak doğru değil. Bir Date nesnesini kopyalamayı denerseniz, Date yapıcı işlevine yapılan çağrı yeni Date öğesini geçerli tarih / saatle başlattığından aynı tarihi alamazsınız. Bu değer numaralandırılamaz ve for / in döngüsü tarafından kopyalanmaz.
A. Levy

Mükemmel değil, ama bu temel durumlar için güzel. Örneğin, temel bir Nesne, Dizi veya Dize olabilecek bir argümanın basit bir şekilde kopyalanmasına izin vermek.
james_womack

Kullanarak yapıcı doğru çağırmak için yükseltildi new. Kabul edilen cevap vermez.
GetFree

Düğüm üzerinde her şey çalışıyor! hala sol referans bağlantıları
user956584

Özyinelemeli düşünce harika.Ancak değer dizi ise, çalışır mı?
Q10Viking

22

A.Levy'nin cevabı neredeyse tamamlandı, işte benim küçük katkım: özyinelemeli referansları ele almanın bir yolu var, bu satıra bakın

if(this[attr]==this) copy[attr] = copy;

Nesne XML DOM öğesiyse, bunun yerine cloneNode kullanmalıyız

if(this.cloneNode) return this.cloneNode(true);

A.Levy'nin kapsamlı çalışmasından ve Calvin'in prototipleme yaklaşımından esinlenerek şu çözümü sunuyoruz:

Object.prototype.clone = function() {
  if(this.cloneNode) return this.cloneNode(true);
  var copy = this instanceof Array ? [] : {};
  for(var attr in this) {
    if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
      copy[attr] = this[attr];
    else if(this[attr]==this) copy[attr] = copy;
    else copy[attr] = this[attr].clone();
  }
  return copy;
}

Date.prototype.clone = function() {
  var copy = new Date();
  copy.setTime(this.getTime());
  return copy;
}

Number.prototype.clone = 
Boolean.prototype.clone =
String.prototype.clone = function() {
  return this;
}

Cevaplardaki Andy Burke'nin notuna da bakınız.


3
Date.prototype.clone = function() {return new Date(+this)};
RobG

22

Bu makalede: Javascript Brian Huisman tarafından diziler ve nesneler nasıl kopyalanır :

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

4
Bu yakındır, ancak herhangi bir nesne için çalışmaz. Bununla bir Date nesnesi kopyalamayı deneyin. Tüm özellikler numaralandırılamaz, bu nedenle hepsi for / in döngüsünde görünmez.
A. Levy

Bunun gibi nesne prototipine eklemek benim için jQuery'i kırdı. Klon2 olarak yeniden adlandırdığımda bile.
iPadDeveloper2011

3
@ iPadDeveloper2011 Yukarıdaki kodda, '(bu var i için)' yerine 'i' '(buradaki i için)' adlı global bir değişken yarattığı bir hata vardı. Düzenlemek için yeterli karmam var ve düzeltip düzelttim.
mikemaccana

1
@Calvin: Bu numaralandırılamayan bir özellik oluşturulmalıdır, aksi takdirde 'for' döngülerinde 'klon' görünecektir.
mikemaccana

2
neden var copiedObj = Object.create(obj);harika bir yol değil ?
Dan P.

19

ES-6'da Object.assign (...) işlevini kullanabilirsiniz. Ör:

let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);

İyi bir referans burada: https://googlechrome.github.io/samples/object-assign-es6/


12
Nesneyi derinlemesine klonlamaz.
Ağustos

Bu bir ödev, kopya değil. clone.Title = "sadece bir klon" obj.Title = "sadece bir klon" anlamına gelir.
HoldOffHunger

@HoldOffHunger Yanılıyorsunuz. Tarayıcınızın JS konsolunda kontrol edin ( let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj); clone.title = "Whazzup";)
daraltı

@collapsar: Tam olarak kontrol ettiğim şey bu, o zaman console.log (kişi) "Thor Odinson" değil, "Whazzup" olacak. Ağustos ayının yorumuna bakın.
HoldOffHunger

1
@HoldOffHunger Chrome 60.0.3112.113 veya Edge 14.14393'te gerçekleşmez; Ağustos'un yorumu, ilkel tiplerin objözelliklerinin değerleri gerçekten klonlandığı için geçerli değildir. Nesnelerin kendileri olan özellik değerleri klonlanmayacaktır.
çöküş

18

ECMAScript 2018'de

let objClone = { ...obj };

Unutmayın iç içe nesneleri hala kopyalandığı bir referans olarak.


1
Yuvalanmış nesnelerin hala referans olarak kopyalandığı ipucu için teşekkürler! "Klon" iç içe özelliklerini değiştirdi çünkü orijinal hata değiştirildi çünkü benim kod hata ayıklama neredeyse deli var.
Benny Neugebauer

Bu 2018 değil ES2016 ve bu cevap iki yıl önce verildi .
Dan Dascalescu

ne de ben de iç içe mülkiyet kopyasını istiyorsanız
Sunil Garg

1
@SunilGarg İç içe geçmiş özelliği de kopyalamak için kullanabilirsinizconst objDeepClone = JSON.parse(JSON.stringify(obj));
Pavan Garre

16

Lodash kullanma:

var y = _.clone(x, true);

5
OMG klonlamayı yeniden icat etmek delilik olur. Tek aklı başında cevap bu.
Dan Ross

5
_.cloneDeep(x)Aslında yukarıdakiyle aynı şey olduğunu, ancak daha iyi okuduğunu tercih ederim .
14:14, garbanzio


13

Tek bir kod satırı kullanarak bir nesneyi klonlayabilir ve öncekinden herhangi bir referansı kaldırabilirsiniz. Basitçe yapın:

var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references

obj2.text = 'moo2'; // Only updates obj2's text property

console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}

Şu anda Object.create özelliğini desteklemeyen tarayıcılar / motorlar için bu çoklu dolguyu kullanabilirsiniz:

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}

1
+1 Object.create(...)kesinlikle gitmek için bir yol gibi görünüyor.
René Nyffenegger

Mükemmel cevap. Belki bir açıklama ekleyebilirsiniz Object.hasOwnProperty? Bu şekilde insanlar prototip bağlantısında arama yapmayı nasıl engelleyeceklerini bilirler.
froginvasion

İyi çalışıyor, ancak çoklu dolgu hangi tarayıcılarda çalışıyor?
Ian Lunn

11
Bu prototip olarak obj1 ile obj2 yaratıyor. Yalnızca textobj2'de üyeyi gölgelediğiniz için çalışır . Bir kopya oluşturmuyorsunuz, sadece obj2'de bir üye bulunmadığında prototip zincirine erteliyorsunuz.
Nick Desaulniers

2
Bu, onu "referanslar olmadan" oluşturmaz, sadece referansı prototipe taşır. Hala bir referans. Orijinalde bir özellik değişirse, "klon" daki prototip özelliği de değişecektir. Hiç bir klon değil.
Jimbo Jonny

13

Eski bir soruya yeni cevap! ECMAScript 2016'yı (ES6) Spread Syntax ile kullanmaktan zevk alıyorsanız , bu kolay.

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}

Bu, bir nesnenin sığ bir kopyası için temiz bir yöntem sağlar. Derin bir kopya yapmak, yani her özyinelemeli iç içe nesne içindeki her değerin yeni bir kopyasını yapmak, yukarıdaki daha ağır çözümlere ihtiyaç duyar.

JavaScript gelişmeye devam ediyor.


2
nesnelerde tanımlanmış işlevleriniz olduğunda çalışmaz
Petr Marek

gördüğüm kadarıyla forma operatörü sadece yinelenebilir ile çalışır - developer.mozilla.org diyor ki: var obj = {'key1': 'value1'}; var array = [...obj]; // TypeError: obj is not iterable
Oleh

@Oleh bu yüzden [... obj] yerine `{... obj} 'i kullanın;
manikant gautam

@manikantgautam Daha önce Object.assign () kullanıyordum, ancak şimdi en son Chrome, Firefox'ta (hala Edge ve Safari'de değil) nesne yayılımı sözdizimi destekleniyor. ECMAScript önerisi ... ancak Babel görebildiğim kadarıyla destekliyor, bu yüzden muhtemelen kullanmak güvenlidir.
Oleh

12
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

Yalnızca bir özellik nesnesini değil, bir sınıf örneğini (sığ) klonlamak istiyorsanız ES6 çözümü .


Bunun farkı let cloned = Object.assign({}, obj)nedir?
ceztko

10

Bence basit ve çalışan bir cevap var. Derin kopyalamada iki endişe vardır:

  1. Özellikleri birbirinden bağımsız tutun.
  2. Ve klonlanmış nesne üzerinde yöntemleri canlı tutun.

Bence basit bir çözüm ilk önce serileştirmek ve serisini kaldırmak ve sonra da işlevleri kopyalamak için bir atama yapmak olacaktır.

let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);

Bu sorunun birçok yanıtı olmasına rağmen, umarım bu da yardımcı olur.


Lodash ithal etmeme izin verilirse, lodash kullanmayı tercih ederim cloneDeep.
ConductedClever

2
JSON.parse (JSON.stringify (kaynak)) kullanıyorum. Daima çalışıyor.
Misha

2
@Misha, bu şekilde fonksiyonları özleyeceksin. 'Eserler' teriminin birçok anlamı vardır.
ConductedClever

Ve bahsettiğim gibi, sadece ilk katmanın işlevlerinin kopyalanacağını unutmayın. Yani birbirimizin içinde bazı nesneler varsa, tek yol alanlarını tekrar tekrar kopyalamaktır.
ConductedClever

9

Derin bir kopya ve klon için JSON.stringify sonra JSON. nesneyi ayrıştırın:

obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}

oldukça zeki ... bu yaklaşımın herhangi bir dezavantajı var mı?
Aleks

8

Bu, A. Levy kodunun işlevlerin ve çoklu / döngüsel referansların klonlanmasını da ele almak için bir uyarlamasıdır - bunun anlamı, klonlanan ağaçtaki iki özellik aynı nesnenin referanslarıysa, klonlanan nesne ağacının bunlara sahip olacağıdır. özellikleri başvurulan nesnenin bir ve aynı klonunu gösterir. Bu aynı zamanda, ele alınmazsa sonsuz bir döngüye yol açan döngüsel bağımlılıklar durumunu da çözer. Algoritmanın karmaşıklığı O (n) 'dir

function clone(obj){
    var clonedObjectsArray = [];
    var originalObjectsArray = []; //used to remove the unique ids when finished
    var next_objid = 0;

    function objectId(obj) {
        if (obj == null) return null;
        if (obj.__obj_id == undefined){
            obj.__obj_id = next_objid++;
            originalObjectsArray[obj.__obj_id] = obj;
        }
        return obj.__obj_id;
    }

    function cloneRecursive(obj) {
        if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;

        // Handle Date
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handle Array
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0; i < obj.length; ++i) {
                copy[i] = cloneRecursive(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            if (clonedObjectsArray[objectId(obj)] != undefined)
                return clonedObjectsArray[objectId(obj)];

            var copy;
            if (obj instanceof Function)//Handle Function
                copy = function(){return obj.apply(this, arguments);};
            else
                copy = {};

            clonedObjectsArray[objectId(obj)] = copy;

            for (var attr in obj)
                if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                    copy[attr] = cloneRecursive(obj[attr]);                 

            return copy;
        }       


        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    var cloneObj = cloneRecursive(obj);



    //remove the unique ids
    for (var i = 0; i < originalObjectsArray.length; i++)
    {
        delete originalObjectsArray[i].__obj_id;
    };

    return cloneObj;
}

Bazı hızlı testler

var auxobj = {
    prop1 : "prop1 aux val", 
    prop2 : ["prop2 item1", "prop2 item2"]
    };

var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;

obj.f1 = function (){
    this.prop1 = "prop1 val changed by f1";
};

objclone = clone(obj);

//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));

objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));

1
Eylül 2016 itibarı ile bu sorunun tek doğru çözümü budur.
DomQ

6

Sadece tüm Object.create bu yazıdaki çözümlere , bunun nodejs ile istenen şekilde çalışmadığını.

Firefox’ta

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´

dır-dir

{test:"test"}.

Düğümlerde

{}

Bu prototip miras, klonlama değil.
d13

1
@ d13 bağımsız değişkeniniz geçerliyken, JavaScript'te bir nesneyi klonlamanın standart bir yolu olmadığını unutmayın. Bu prototipsel kalıtımdır, ancak kavramları anlarsanız yine de klon olarak kullanılabilir.
froginvasion

@froginvasion. Object.create kullanmanın tek sorunu, iç içe geçmiş nesnelerin ve dizilerin prototipin iç içe geçmiş nesnelerine ve dizilerine yalnızca işaretçi referansları olmasıdır. jsbin.com/EKivInO/2/edit?js,console . Teknik olarak "klonlanmış" bir nesnenin, diğer nesnelerdeki özelliklere paylaşılan referanslar olmayan kendi benzersiz özellikleri olmalıdır.
d13

@ d13 tamam, şimdi anlıyorum. Ama demek istediğim, çok fazla insanın prototipsel miras kavramıyla yabancılaşmış olması ve benim için bunun nasıl çalıştığını öğrenememesi. Yanılmıyorsam, örneğin Object.hasOwnPropertydizinin sahibi olup olmadığınızı kontrol etmek için arayarak örneğiniz düzeltilebilir . Evet, bu prototip mirasla başa çıkmak için ek karmaşıklık getirir.
froginvasion

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

2
if(!src && typeof src != "object"){. Bunun olması gerektiğini düşünüyorum ||değildir &&.
MikeM

6

Yana mindeavor nesnesi klonlanan 'değişmez inşa' nesne olduğunu ifade eden bir çözelti, basitçe olabilir oluşturmak nesnesinin bir örneğini klonlama daha nesne birden çok kez yerine:

function createMyObject()
{
    var myObject =
    {
        ...
    };
    return myObject;
}

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();

6

Kendi uygulamamı yazdım. Daha iyi bir çözüm olarak kabul edilip edilmediğinden emin değilim:

/*
    a function for deep cloning objects that contains other nested objects and circular structures.
    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                    index (z)
                                         |
                                         |
                                         |
                                         |
                                         |
                                         |                      depth (x)
                                         |_ _ _ _ _ _ _ _ _ _ _ _
                                        /_/_/_/_/_/_/_/_/_/
                                       /_/_/_/_/_/_/_/_/_/
                                      /_/_/_/_/_/_/...../
                                     /................./
                                    /.....            /
                                   /                 /
                                  /------------------
            object length (y)    /
*/

Uygulama aşağıdadır:

function deepClone(obj) {
    var depth = -1;
    var arr = [];
    return clone(obj, arr, depth);
}

/**
 *
 * @param obj source object
 * @param arr 3D array to store the references to objects
 * @param depth depth of the current object relative to the passed 'obj'
 * @returns {*}
 */
function clone(obj, arr, depth){
    if (typeof obj !== "object") {
        return obj;
    }

    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'

    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
    if(result instanceof Array){
        result.length = length;
    }

    depth++; // depth is increased because we entered an object here

    arr[depth] = []; // this is the x-axis, each index here is the depth
    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
    // start the depth at current and go down, cyclic structures won't form on depths more than the current one
    for(var x = depth; x >= 0; x--){
        // loop only if the array at this depth and length already have elements
        if(arr[x][length]){
            for(var index = 0; index < arr[x][length].length; index++){
                if(obj === arr[x][length][index]){
                    return obj;
                }
            }
        }
    }

    arr[depth][length].push(obj); // store the object in the array at the current depth and length
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
    }

    return result;
}

Benim durumum biraz karmaşık olmasına rağmen, benim nesne için çalışmıyor.
Sajuuk
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.