2105 votes

Copie d'un tableau par valeur en javascript

Lors de la copie d'un tableau en javascript vers un autre tableau :

var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d');  //now, arr1 = ['a','b','c','d']

J'ai réalisé que arr2 fait référence au même tableau que arr1 plutôt qu'un nouveau tableau indépendant. Comment puis-je copier le tableau pour obtenir deux tableaux indépendants ? L'utilisation de jQuery serait idéale.

4 votes

Il semble qu'actuellement, dans Chrome 53 et Firefox 48, les performances sont bonnes pour slice y splice opérations et nouvel opérateur d'épandage et Array.from ont une mise en œuvre beaucoup plus lente. Regardez perfjs.fnfo

0 votes

jsben.ch/#/wQ9RU <= ce benchmark donne un aperçu des différentes manières de copier un tableau.

0 votes

3102voto

Saket Points 9771

Utilisez ça :

var arr2 = arr1.slice(0);

En gros, le tranche() clone le tableau et renvoie la référence au nouveau tableau. Notez également que :

  • Pour les références d'objets (et non l'objet réel), slice copie les références d'objets dans le nouveau tableau. L'original et le nouveau tableau font tous deux référence au même objet. Si un objet référencé change, les changements sont visibles à la fois dans le nouveau tableau et dans le tableau original.
  • Pour les chaînes de caractères et les nombres, slice copie les chaînes de caractères et les nombres dans le nouveau tableau. Les modifications apportées à la chaîne ou au nombre dans un tableau n'affectent pas l'autre tableau.

9 votes

En ce qui concerne les performances, les tests jsPerf suivants montrent que var arr2 = arr1.slice() est aussi rapide que var arr2 = arr1.concat() ; JSPerf : jsperf.com/copy-array-slice-vs-concat/5 y jsperf.com/copy-simple-array . Le résultat de jsperf.com/array-copy/5 Cela m'a surpris au point que je me demande si le code de test est valide.

117 votes

Même si ce texte a déjà reçu une tonne de votes positifs, il en mérite un autre car il décrit correctement les références en JS, ce qui est assez rare, malheureusement.

38 votes

@GáborImre vous ajouteriez une bibliothèque entière simplement pour la lisibilité ? Vraiment ? Je me contenterais d'ajouter un commentaire si j'étais aussi soucieux de la lisibilité. Voir : var newArray = oldArray.slice() ; //Clone oldArray to newArray

661voto

tfmontague Points 172

Javascript fournit plusieurs types différents de tableaux (au moins 5 types).

var type1 = ['a', 'b']; // Array of Strings
var type2 = [1, 2]; // Array of Numbers
var type3 = [['a'], ['b']]; // Array of Arrays
var type4 = [{a: 'a'} , {b: 'b'}]; // Array of Object-Literals
var type5 = [{a: function () {}}, {b: function () {}}]; // Array of Objects

Selon le type de tableau, diverses techniques (comme .splice, .concat, JSON, $.extend, etc.) peuvent être utilisées pour copier profondément un tableau.

myArray.splice(0);
myArray.concat();
JSON.parse(JSON.stringify(myArray));
$.extend(true, [], myArray); // jQuery
_.extend(); // underscore
_.cloneDeep(); // lo-dash

Cependant, la plupart des techniques ne permettent pas de copier en profondeur tous les types de tableaux.

Prise en charge de la copie profonde pour diverses techniques (par type de tableau) Deep-copy technique by array-type

  • Splice et Concat peuvent être utilisés pour copier en profondeur un tableau de chaînes de caractères et un tableau de nombres, Splice étant plus performant que Concat. http://jsperf.com/duplicate-array-slice-vs-concat/3
  • JSON.parse(JSON.stringify()) peut être utilisé pour copier en profondeur un tableau de chaînes de caractères, un tableau de nombres, un tableau de matrices et un tableau de littéraux d'objets, mais pas un tableau d'objets prototypes.
  • jQuery $.extend() peut être utilisé pour copier en profondeur tout type de tableau. D'autres bibliothèques, comme underscore et lo-dash, offrent des fonctions de copie profonde similaires, mais leurs performances sont également plus faibles. Plus surprenant, $.extend a également de meilleures performances que JSON.parse(JSON.stringify()). http://jsperf.com/js-deep-copy/2 ,

Copie profonde de tout type de tableau (sans bibliothèque tierce) :

Et pour les développeurs qui hésitent à utiliser des bibliothèques tierces (comme jQuery), la fonction personnalisée suivante peut être utilisée à la place. Elle offre des performances plus rapides que $.extend, et copie en profondeur tous les types de tableaux.

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

3 votes

Beaucoup de ces approches ne fonctionnent pas bien. L'utilisation de l'opérateur d'affectation signifie que vous devez réaffecter la valeur littérale originale de l'élément arr1 . Il est très rare que ce soit le cas. Utilisation de splice oblitère arr1 Il ne s'agit donc pas du tout d'une copie. Utilisation de JSON échouera si l'une des valeurs du tableau est une fonction ou possède des prototypes (comme un Date ).

0 votes

L'utilisation de splice est une solution partielle. Elle échouera dans bien plus de cas que JSON. Splice crée une copie profonde des chaînes de caractères et des nombres, lorsqu'il déplace les valeurs - il n'a jamais dit qu'il retournait une copie.

1 votes

Pourquoi splice(0) ? Ne devrait-on pas plutôt dire slice() ? Je pense que c'est censé ne pas modifier le tableau original, ce que fait splice. @JamesMontagne

165voto

jondavidjohn Points 28769

Pas besoin de jQuery... Exemple de travail

var arr2 = arr1.slice()

Ceci copie le tableau à partir de la position de départ 0 jusqu'à la fin du tableau.

Il est important de noter que cela fonctionnera comme prévu pour les types primitifs (chaîne, nombre, etc.), et d'expliquer également le comportement attendu pour les types de référence...

Si vous avez un tableau de types de référence, disons de type Object . Le tableau sera être copiés, mais les deux tableaux contiendront des références à la même Object 's. Ainsi, dans ce cas, il semblerait que le tableau soit copié par référence même si le tableau est effectivement copié.

18 votes

Non, ce ne serait pas une copie profonde.

1 votes

Essayez ça ; var arr2 = JSON.stringify(arr1); arr2 = JSON.parse(arr2);

4 votes

Quelle est la différence entre cette réponse et la réponse acceptée ?

77voto

Ninjakannon Points 593

Je l'ai déjà utilisé dans le passé :

var array2 = [].concat(array1);

Je pense que c'est aussi bien que d'utiliser la tranche mais plus lisible, donc je ne sais pas pourquoi cela n'a pas été suggéré. Peut-être qu'il me manque un inconvénient ?

31 votes

En fait, vous pouvez aussi faire : var array2 = array1.concat() ; C'est beaucoup plus rapide en termes de performances. (JSPerf : jsperf.com/copy-simple-array y jsperf.com/copy-array-slice-vs-concat/5

5 votes

Il est intéressant de noter que si array1 n'est pas un tableau, alors [].concat(array1) renvoie à [array1] par exemple, si elle est indéfinie, vous obtiendrez [undefined] . Je fais parfois var array2 = [].concat(array1 || []);

22voto

sarvesh singh Points 159

Les méthodes mentionnées ci-dessus fonctionnent bien lorsqu'on travaille avec des types de données simples comme des nombres ou des chaînes de caractères, mais lorsque le tableau contient d'autres objets, ces méthodes échouent. Lorsque nous essayons de faire passer un objet d'un tableau à un autre, il est transmis comme une référence et non comme un objet.

Ajoutez le code suivant dans votre fichier js :

Object.prototype.clone = function() {
  var newObj = (this instanceof Array) ? [] : {};
  for (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;
};

et utiliser simplement

var arr1 = ['val_1','val_2','val_3'];
var arr2 = arr1.clone()

Ça va marcher.

2 votes

J'obtiens cette erreur lorsque j'ajoute ce code à ma page 'Uncaught RangeError : Taille maximale de la pile d'appels dépassée'.

1 votes

Je m'excuse, cette erreur se produit dans chrome si arr1 n'est pas déclaré. J'ai donc copié-collé le code ci-dessus, et j'ai l'erreur, cependant, si je déclare le tableau arr1, alors je n'ai pas l'erreur. Vous pourriez améliorer la réponse en déclarant arr1 juste au-dessus de arr2, je vois qu'un certain nombre d'entre "nous" n'ont pas reconnu qu'il fallait déclarer arr1 (en partie parce que lorsque j'ai évalué votre réponse, j'étais pressé et j'avais besoin de quelque chose qui "fonctionne").

0 votes

.slice() fonctionne toujours bien, même si vous avez des objets dans votre tableau : jsfiddle.net/edelman/k525g

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X