web-dev-qa-db-fra.com

Est-ce un bon moyen de cloner un objet dans ES6?

Googler pour "javascript clone object" donne des résultats vraiment bizarres, certains sont désespérément obsolètes et d'autres trop complexes, n'est-ce pas aussi simple que cela:

let clone = {...original};

Y a-t-il un problème avec ça?

125
Dmitry Fadeev

C'est bon pour le clonage superficiel . Le la propagation d'objet est une partie standard d'ECMAScript 2018 .

Pour le clonage en profondeur, vous aurez besoin de solution différente .

const clone = {...original} à cloner peu profond

const newobj = {...original, prop: newOne} pour ajouter immuablement un autre accessoire à l'original et le stocker en tant que nouvel objet.

196
Mark Shust

EDIT: Lorsque cette réponse a été publiée, la syntaxe {...obj} n'était pas disponible dans la plupart des navigateurs. De nos jours, vous devriez pouvoir l'utiliser (à moins que vous n'ayez besoin de supporter IE 11).

Utilisez Object.assign.

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 }

Cependant, cela ne fera pas un clone profond. Il n’existe pas encore de méthode autochtone de clonage en profondeur.

EDIT: Comme @Mike 'Pomax' Kamermans est mentionné dans les commentaires, vous pouvez profondément cloner des objets simples (c'est-à-dire sans prototypes, fonctions ou références circulaires) en utilisant JSON.parse(JSON.stringify(input))

59
Alberto Rivera

si vous ne souhaitez pas utiliser json.parse (json.stringify (objet)), vous pouvez créer de manière récursive des copies de valeurs-clés:

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

Mais le meilleur moyen est de créer une classe pouvant renvoyer un clone

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) }
}
3
marcel

Si les méthodes que vous avez utilisées ne fonctionnent pas bien avec des objets impliquant des types de données tels que Date, essayez ceci.

Importer _

import * as _ from 'lodash';

Objet clone profond

myObjCopy = _.cloneDeep(myObj);
2
shaheer shukur

Suite à la réponse de @marcel, j’ai trouvé que certaines fonctions manquaient encore sur l’objet cloné. par exemple.

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 || {};
    }
  });
}

où sur MyObject je pouvais cloner methodA mais methodB était exclu. Ceci est dû au fait qu'il manque

enumerable: true

ce qui signifiait qu'il ne figurait pas dans

for(let key in item)

Au lieu de cela je suis passé à

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

qui comprendra des clés non énumérables.

J'ai également constaté que le prototype (proto) n'était pas cloné. Pour cela, j'ai fini par utiliser

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

PS: Frustrant que je ne puisse pas trouver une fonction intégrée pour le faire.

2
Shane Gannon

Toutes les méthodes ci-dessus ne gèrent pas le clonage profond d'objets où il est imbriqué sur n niveaux. Je n'ai pas vérifié ses performances par rapport aux autres mais c'est court et simple.

Le premier exemple ci-dessous montre le clonage d'objet à l'aide de Object.assign qui clone jusqu'au premier niveau.

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

Utilisation de l'objet d'approche profonde ci-dessous

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
0
Saksham