Bu, ES6'da bir nesneyi klonlamanın iyi bir yolu mu?


155

"Javascript klon nesnesi" için googling bazı gerçekten garip sonuçlar getiriyor, bazıları umutsuzca modası geçmiş ve bazıları çok karmaşık, o kadar kolay değil:

let clone = {...original};

Bunda yanlış bir şey var mı?


1
bu yasal ES6 değil. Ama eğer öyleyse, bu bir klon değildir: hem klonunuz hem de orijinal özellikleriniz aynı şeyleri gösteriyor. Örneğin, original = { a: [1,2,3] }size clone.atam anlamıyla var olan bir klon verir original.a. Ya yoluyla Modifikasyon cloneveya originaltuşelere aynı şey , bu yüzden hayır, bu) kötü = olduğunu
Mike 'Po max' Kamermans

2
@AlbertoRivera Bu biraz geçerli bir JavaScript, çünkü JavaScript standardına gelecekte eklenecek bir aşama 2 önerisi.
Frxstrem

@Frxstrem ES6 ile ilgili soru, bu geçerli değil JavaScript =)
Mike 'Pomax' Kamermans

3
Sığ veya derin klonlama?
Felix Kling

2
Haklısın, geçerli ES6 değil, geçerli ES9 . developer.mozilla.org/tr-TR/docs/Web/JavaScript/Reference/…
mikemaccana

Yanıtlar:


240

Bu sığ klonlama için iyidir . Nesne yayılması ECMAScript'e 2018 standart bir parçası olduğunu .

Derin klonlama için farklı bir çözüme ihtiyacınız olacak .

const clone = {...original} sığ klonlamak

const newobj = {...original, prop: newOne} orijinaline değişmez bir şekilde başka bir pervane eklemek ve yeni bir nesne olarak saklamak.


18
Ancak, bu sadece sığ bir klon değil mi? İçinde olduğu gibi, özellikler özyinelemeli olarak klonlanmaz, değil mi? Bu nedenle, original.innerObject === clone.innerObject ve original.innerObject.property öğelerini değiştirmek clone.innerObject.property öğesini değiştirir.
milanio

18
evet, bu sığ bir klon. derin bir klon istiyorsanızJSON.parse(JSON.stringify(input))
Mark Shust M.academy'de

8
/! \ JSON.parse (JSON.stringify (giriş)) tarihler berbat, tanımsız, ... Klonlama için gümüş mermi değil! Bakınız: maxpou.fr/immutability-js-without-library
Guillaume

1
Peki JSON.stringify () / JSON.parse () hack gerçekten ES6'da bir nesneyi derin klonlamak için önerilen yol mu? Tavsiye görmeye devam ediyorum. Rahatsız edici.
Solvitieg

3
@MarkShust JSON.parse(JSON.stringify(input))çalışmaz, çünkü orada functionsya infinityda değer olarak nullkendi yerine atar . Yalnızca değerler basitse literalsve değilse çalışır functions.
backslashN

66

DÜZENLE: Bu yanıt gönderildiğinde, {...obj}sözdizimi çoğu tarayıcıda mevcut değildi. Günümüzde (IE 11'i desteklemeniz gerekmedikçe) bunu kullanmalısınız.

Object.assign öğesini kullanın.

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

var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }

Ancak, bu derin bir klon oluşturmaz. Henüz derin klonlamanın doğal bir yolu yok.

DÜZENLEME: @Mike 'Pomax' Kamermans'ın yorumlarda belirtildiği gibi, basit nesneleri (yani, prototip, fonksiyon veya dairesel referans yok) kullanarak klonlayabilirsiniz. JSON.parse(JSON.stringify(input))


19
Nesnenizin gerçek bir nesne değişmezi ve tamamen veri olması koşuluyla bir tane var, bu durumda JSON.parse(JSON.stringify(input))uygun bir derin klon. Bununla birlikte, prototipler, fonksiyonlar veya dairesel referanslar yürürlükte olduğu anda, bu çözüm artık çalışmaz.
Mike 'Pomax' Kamermans

@ Mike'Pomax'Kamermans Bu doğru. Ancak, alıcılar ve setters için işlevselliği kaybetmek korkunç ...
Alberto Rivera

Herhangi bir nesneyi klonlamak için genel bir işleve ihtiyacınız varsa stackoverflow.com/a/13333781/560114 adresini ziyaret edin .
Matt Browne

1
Artık doğal olarak derin klonlama yapmanın bir yolu var .
Dan Dascalescu

1
@DanDascalescu deneysel olmasına rağmen oldukça umut verici görünüyor. Bilgi için teşekkürler!
Alberto Rivera

4

Kullandığınız yöntemler Date gibi veri türlerini içeren nesnelerle iyi çalışmıyorsa bunu deneyin

İthalat _

import * as _ from 'lodash';

Derin klon nesnesi

myObjCopy = _.cloneDeep(myObj);

Sadece import _ from 'lodash';yeter. Ancak +1 "tekerleği yeniden icat etme" yanıtı için.
rustyx

lodash şişirilir. Gerçekten sadece basit bir derin kopya için lodash çekmeye gerek yok. Burada birçok çözüm var. Bu, zayıf bir uygulama oluşturmak isteyen web geliştiricileri için gerçekten kötü bir cevaptır.
Jason Rice

3

json.parse (json.stringify (object)) kullanmak istemiyorsanız, yinelenen anahtar / değer kopyaları oluşturabilirsiniz:

function copy(item){
  let result = null;
  if(!item) return result;
  if(Array.isArray(item)){
    result = [];
    item.forEach(element=>{
      result.push(copy(element));
    });
  }
  else if(item instanceof Object && !(item instanceof Function)){ 
    result = {};
    for(let key in item){
      if(key){
        result[key] = copy(item[key]);
      }
    }
  }
  return result || item;
}

Ama en iyi yol, kendisinin bir klonunu döndürebilen bir sınıf yaratmaktır

class MyClass{
    data = null;
    constructor(values){ this.data = values }
    toString(){ console.log("MyClass: "+this.data.toString(;) }
    remove(id){ this.data = data.filter(d=>d.id!==id) }
    clone(){ return new MyClass(this.data) }
}

2

@Marcel'in cevabından sonra klonlanmış nesnede bazı fonksiyonların hala eksik olduğunu gördüm. Örneğin

function MyObject() {
  var methodAValue = null,
      methodBValue = null

  Object.defineProperty(this, "methodA", {
    get: function() { return methodAValue; },
    set: function(value) {
      methodAValue = value || {};
    },
    enumerable: true
  });

  Object.defineProperty(this, "methodB", {
    get: function() { return methodAValue; },
    set: function(value) {
      methodAValue = value || {};
    }
  });
}

burada MyObject üzerinde methodA klonlayabilirdim ama methodB hariç tutuldu. Bu, eksik olduğu için oluştu

enumerable: true

bu demek değil ki

for(let key in item)

Bunun yerine

Object.getOwnPropertyNames(item).forEach((key) => {
    ....
  });

numaralandırılamayan anahtarları içerir.

Ayrıca prototipin ( proto ) klonlanmadığını gördüm . Bunun için kullanarak

if (obj.__proto__) {
  copy.__proto__ = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
}

Not: Bunu yapmak için yerleşik bir işlev bulamadığım için sinir bozucu.


1

Sen de böyle yapabilirsin,

let copiedData = JSON.parse(JSON.stringify(data));

-1
We can do that with two way:
1- First create a new object and replicate the structure of the existing one by iterating 
 over its properties and copying them on the primitive level.

let user = {
     name: "John",
     age: 30
    };

    let clone = {}; // the new empty object

    // let's copy all user properties into it
    for (let key in user) {
      clone[key] = user[key];
    }

    // now clone is a fully independant clone
    clone.name = "Pete"; // changed the data in it

    alert( user.name ); // still John in the original object

2- Second we can use the method Object.assign for that 
    let user = { name: "John" };
    let permissions1 = { canView: true };
    let permissions2 = { canEdit: true };

    // copies all properties from permissions1 and permissions2 into user
    Object.assign(user, permissions1, permissions2);

  -Another example

    let user = {
      name: "John",
      age: 30
    };

    let clone = Object.assign({}, user);
It copies all properties of user into the empty object and returns it. Actually, the same as the loop, but shorter.

Ancak Object.assign () derin bir klon oluşturmaz

let user = {
  name: "John",
  sizes: {
    height: 182,
    width: 50
  }
};

let clone = Object.assign({}, user);

alert( user.sizes === clone.sizes ); // true, same object

// user and clone share sizes
user.sizes.width++;       // change a property from one place
alert(clone.sizes.width); // 51, see the result from the other one

Bunu düzeltmek için, kullanıcı [anahtar] 'ın her değerini inceleyen klonlama döngüsünü kullanmalıyız ve eğer bir nesne ise yapısını da çoğaltırız. Buna “derin klonlama” denir.

Yukarıdaki durumu ele alan standart bir algoritma ve Yapısal klonlama algoritması adı verilen daha karmaşık vakalar vardır . Tekerleği yeniden icat etmemek için, yöntemin _.cloneDeep (obj) adı verilen JavaScript kütüphanesinden çalışan bir uygulamasını kullanabiliriz .


-1

Yukarıdaki tüm yöntemler, n seviyesine yerleştirildiği nesnelerin derin klonlanmasını işlemez. Performansını diğerlerine göre kontrol etmedim ama kısa ve basit.

Aşağıdaki ilk örnek, Object.assignilk seviyeye kadar hangi klonları kullanarak nesne klonlamasını gösterir .

var person = {
    name:'saksham',
    age:22,
    skills: {
        lang:'javascript',
        experience:5
    }
}

newPerson = Object.assign({},person);
newPerson.skills.lang = 'angular';
console.log(newPerson.skills.lang); //logs Angular

Aşağıdaki yaklaşımı kullanarak derin klonlar nesnesi

var person = {
    name:'saksham',
    age:22,
    skills: {
        lang:'javascript',
        experience:5
    }
}

anotherNewPerson = JSON.parse(JSON.stringify(person));
anotherNewPerson.skills.lang = 'angular';
console.log(person.skills.lang); //logs javascript


JSON.parse / stringify için kötü derin klonlama yöntemi olarak söz edilmiştir yıllar . Lütfen önceki yanıtları ve ilgili soruları kontrol edin. Ayrıca, bu ES6 için yeni bir şey değil.
Dan Dascalescu

@DanDascalescu Bunu biliyorum ve basit nesneler için kullanmak bir sorun olmamalı. Diğerleri de aynı yazıdaki cevaplarında ve hatta yorumlarda belirtmişlerdir. Bence bir düşüşü hak etmiyor.
Saksham

Kesinlikle - "diğerleri de" JSON.parse / stringify yanıtlarında bahsetti. Neden aynı çözümle başka bir cevap daha gönderelim?
Dan Dascalescu
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.