Question Copie de 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 arr1plutôt qu'un nouveau tableau indépendant. Comment puis-je copier le tableau pour obtenir deux tableaux indépendants?


1335
2017-09-20 13:38


origine


Réponses:


Utilisez ceci:

var newArray = oldArray.slice();

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

Pour les références, les chaînes et les nombres (et non l'objet réel), slice copie les références d'objet dans le nouveau tableau. Le tableau original et le nouveau tableau font tous deux référence au même objet. Si un objet référencé est modifié, les changements sont visibles à la fois pour les tableaux nouveaux et originaux.

Les primitives telles que les chaînes et les nombres sont immuables, de sorte qu'il est impossible de modifier la chaîne ou le nombre.


2152
2017-09-20 13:41



En Javascript, les techniques de copie profonde dépendent des éléments d'un tableau.
Commençons là.

Trois types d'éléments

Les éléments peuvent être: des valeurs littérales, des structures littérales ou des prototypes.

// Literal values (type1)
var booleanLiteral = true;
var numberLiteral = 1;
var stringLiteral = 'true';

// Literal structures (type2)
var arrayLiteral = [];
var objectLiteral = {};

// Prototypes (type3)
var booleanPrototype = new Bool(true);
var numberPrototype = new Number(1);
var stringPrototype = new String('true');
var arrayPrototype = new Array();
var objectPrototype = new Object(); # or "new function () {}"

A partir de ces éléments, nous pouvons créer trois types de tableaux.

// 1) Array of literal-values (boolean, number, string) 
var type1 = [true, 1, "true"];

// 2) Array of literal-structures (array, object)
var type2 = [[], {}];

// 3) Array of prototype-objects (function)
var type3 = [function () {}, function () {}];

Les techniques de copie en profondeur dépendent des trois types de tableau

Basé sur les types d'éléments dans le tableau, nous pouvons utiliser diverses techniques pour copier en profondeur.

Javascript deep copy techniques by element types

  • Tableau de valeurs littérales (type1)
    le myArray.splice(0), myArray.slice(), et myArray.concat() les techniques peuvent être utilisées pour copier en profondeur des tableaux avec des valeurs littérales (booléen, nombre et chaîne) seulement; où Slice a une meilleure performance que Concat (http://jsperf.com/duplicate-array-slice-vs-concat/3).

  • Tableau de valeurs littérales (type1) et literal-structures (type2)
    le JSON.parse(JSON.stringify(myArray)) La technique peut être utilisée pour copier en profondeur les valeurs littérales (booléen, nombre, chaîne) et les structures littérales (tableau, objet), mais pas les objets prototypes.

  • Tous les tableaux (type1, type2, type3)
    Le jQuery $.extend(myArray) La technique peut être utilisée pour copier en profondeur tous les types de tableaux. Les bibliothèques aiment Souligner et Lo-dash offrir des fonctions de copie profonde similaires à jQuery  $.extend(), mais ont des performances inférieures. Plus surprenant, $.extend() a des performances supérieures à la JSON.parse(JSON.stringify(myArray)) technique http://jsperf.com/js-deep-copy/15.
    Et pour les développeurs qui se tiennent à l'écart des bibliothèques tierces (comme jQuery), vous pouvez utiliser la fonction personnalisée suivante; qui a des performances supérieures à $ .extend, et copie en profondeur tous les tableaux.

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

Donc, pour répondre à la question ...

Question 

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

J'ai réalisé que arr2 se réfère 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?

Répondre 

Car arr1 est un tableau de valeurs littérales (booléen, nombre ou chaîne), vous pouvez utiliser toute technique de copie en profondeur discutée ci-dessus, où slice a la plus haute performance.

// Highest performance for deep copying literal values
arr2 = arr1.slice();

// Any of these techniques will deep copy literal values as well,
//   but with lower performance.
arr2 = arr1.splice(0);
arr2 = arr1.concat();
arr2 = JSON.parse(JSON.stringify(arr1));
arr2 = $.extend(true, [], arr1); // jQuery.js needed
arr2 = _.extend(arr1); // Underscore.js needed
arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
arr2 = copy(arr1); // Custom-function needed - as provided above

363
2018-05-08 08:36



Pas de jQuery nécessaire ... Exemple de travail

var arr2 = arr1.slice()

Ceci copie le tableau de la position de départ 0 à travers la fin du tableau.

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

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


146
2017-09-20 13:39



Vous pouvez utiliser des spreads de tableau ... copier des tableaux.

const itemsCopy = [...items];

Aussi, si vous voulez créer un nouveau tableau avec celui qui en fait partie:

var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];

Les spreads de tableau sont maintenant pris en charge dans tous les principaux navigateurs mais si vous avez besoin d'un support plus ancien, utilisez tapuscript ou babel et compilez en ES5.

Plus d'infos sur les spreads


136
2017-07-03 14:46



Une alternative à slice est concat, qui peut être utilisé de 2 façons. Le premier d'entre eux est peut-être plus lisible que le comportement prévu est très clair:

var array2 = [].concat(array1);

La deuxième méthode est:

var array2 = array1.concat();

Cohen (dans les commentaires) a souligné que cette dernière méthode a de meilleures performances.

La façon dont cela fonctionne est que le concat method crée un nouveau tableau composé des éléments de l'objet sur lequel il est appelé suivi par les éléments de tous les tableaux passés en arguments. Donc, quand aucun argument n'est passé, il copie simplement le tableau.

Lee Penkman, également dans les commentaires, souligne que s'il y a une chance array1 est undefined, vous pouvez retourner un tableau vide comme suit:

var array2 = [].concat(array1 || []);

Ou, pour la deuxième méthode:

var array2 = (array1 || []).concat();

Notez que vous pouvez également le faire avec slice: var array2 = (array1 || []).slice();.


67
2017-10-18 00:11



C'est ainsi que je l'ai fait après avoir essayé plusieurs approches:

var newArray = JSON.parse(JSON.stringify(orgArray));

Cela va créer une nouvelle copie profonde non liée à la première (pas une copie superficielle).

Aussi, cela ne clonera évidemment pas les événements et les fonctions, mais la bonne chose que vous pouvez faire en une ligne, et il peut être utilisé pour n'importe quel type d'objet (tableaux, chaînes, nombres, objets ...)


39
2018-04-23 13:32



Certaines des méthodes mentionnées fonctionnent bien lorsque vous travaillez avec des types de données simples comme nombre ou chaîne, mais lorsque le tableau contient d'autres objets, ces méthodes échouent. Lorsque nous essayons de passer un objet d'un tableau à un autre, il est passé en référence, pas en tant qu'objet.

Ajoutez le code suivant dans votre fichier JavaScript:

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 utilisez simplement

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

Ça va marcher.


17
2017-12-12 16:22



D'ES2015,

var arr2 = [...arr1];

14
2017-12-24 07:14



Je pense personnellement Array.from est une solution plus lisible. Soit dit en passant, méfiez-vous de son support de navigateur.

//clone
let x = [1,2,3];
let y = Array.from(x);

//deep clone
let clone = arr => Array.from(arr,item => Array.isArray(item) ? clone(item) : item);
let x = [1,[],[[]]];
let y = clone(x);

14
2018-03-09 12:01



Si vous êtes dans un environnement de ECMAScript 6, en utilisant le Opérateur de propagation vous pourriez le faire de cette façon:

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

console.log(arr1)
console.log(arr2)
<script src="http://www.wzvang.com/snippet/ignore_this_file.js"></script>


10
2017-07-03 09:31