web-dev-qa-db-fra.com

Différence entre l’utilisation d’une syntaxe de propagation (...) et de Push.apply lorsqu’il s’agit de tableaux

J'ai deux tableaux,

const pets = ["dog", "cat", "hamster"]

const wishlist = ["bird", "snake"]

Je veux ajouter wishlist à pets, ce qui peut être fait en utilisant deux méthodes,

Méthode 1:  

pets.Push.apply(pets,wishlist)

Ce qui donne: [ 'dog', 'cat', 'hamster', 'bird', 'snake' ]

Méthode 2:

pets.Push(...wishlist)

Ce qui entraîne également: [ 'dog', 'cat', 'hamster', 'bird', 'snake' ]

Existe-t-il une différence de performance entre ces deux méthodes lorsque je traite des données plus volumineuses?

21
Net Myth

Function.prototype.apply et la syntaxe spread peuvent provoquer un débordement de pile lorsqu'ils sont appliqués à des tableaux de grande taille:

let xs = new Array(500000),
 ys = [], zs;

xs.fill("foo");

try {
  ys.Push.apply(ys, xs);
} catch (e) {
  console.log("apply:", e.message)
}

try {
  ys.Push(...xs);
} catch (e) {
  console.log("spread:", e.message)
}

zs = ys.concat(xs);
console.log("concat:", zs.length)

Utilisez Array.prototype.concat à la place. En plus d'éviter les débordements de pile, concat a l'avantage d'éviter les mutations. Les mutations sont considérées comme nuisibles, car elles peuvent entraîner des effets secondaires subtils.

Mais ce n'est pas un dogme. Si vous utilisez une étendue de fonction et effectuez des mutations pour améliorer les performances et soulager la récupération de place, vous pouvez effectuer des mutations, tant qu'elles ne sont pas visibles dans l'étendue parent.

18
user6445533

Mis à part ce que ftor a souligné , Array.prototype.concat est en moyenne au moins 1,4 fois plus rapide que l'opérateur d'étalement de matrice.

Voir les résultats ici: https://jsperf.com/es6-add-element-tcreate-new-array-concat-vs-spread-op

Vous pouvez exécuter le test sur votre propre navigateur et sur votre machine ici: https://www.measurethat.net/Benchmarks/Show/579/1/arrayprototypeconcat-vs-spread-operator

2
Liau Jian Jie

En interprétant la question comme étant which is more performant in general, using .Push() as an example, il semble que apply soit légèrement plus rapide (sauf pour MS Edge, voir ci-dessous).

Voici un test de performance sur la surcharge liée à l'appel dynamique d'une fonction pour les deux méthodes.

function test() { console.log(arguments[arguments.length - 1]); }
var using = (new Array(200)).fill(null).map((e, i) => (i));

test(...using);

test.apply(null, using)

J'ai testé dans Chrome 71.0.3578.80 (Official Build) (64-bit), FF 63.0.3 (64-bit), & Edge 42.17134.1.0, et ce sont mes résultats après les avoir exécutés plusieurs fois par eux-mêmesLes premiers résultats ont toujours été biaisés d'une manière ou d'une autre

 benchmark results for the three browsers

Comme vous pouvez le constater, Edge semble avoir une meilleure implémentation pour apply que it pour ... (mais n'essayez pas de comparer les résultats d'un navigateur à l'autre, nous ne pouvons pas dire si Edge a une meilleure apply que les autres, ... pire, ou un peu des deux à partir de ces données).
Compte tenu de cela, à moins que vous ne cibliez spécifiquement Edge, je dirais que vous devez utiliser ... comme il se lit plus clair, en particulier si vous devez renvoyer un objet à apply pour this.

Il est également possible que cela dépende de la taille du tableau, aussi, comme @Jaromanda X dit, faites vos propres tests et changez 200 si vous devez vraiment vous en assurer.


Les autres réponses ont interprété la question comme suit: which would be better for .Push() specifically et se sont laissé prendre par le "problème" à résoudre, en recommandant simplement just use .concat(), qui est essentiellement le why are you doing it that way? standard qui peut irriter certaines personnes de Google qui ne cherchent pas de solutions .Push() (par exemple, Math.max ou votre propre fonction personnalisée).

1
Hashbrown

Avec Push, vous ajoutez au tableau existant, avec l'opérateur spread, vous créez une copie.

a=[1,2,3]
b=a
a=[...a, 4]
alert(b);

=> 1, 2, 3

 a=[1,2,3]
b=a
a.Push(4)
alert(b);

=> 1, 2, 3, 4

Push.apply aussi:

a=[1,2,3]
c=[4]
b=a
Array.prototype.Push.apply(a,c)
alert(b);

=> 1, 2, 3, 4

concat est une copie

a=[1,2,3]
c=[4]
b=a
a=a.concat(c)
alert(b);

=> 1, 2, 3

Par référence est préférable, en particulier pour les tableaux de grande taille. 

L'opérateur Spread est un moyen rapide de faire une copie, ce qui se fait traditionnellement avec quelque chose comme:

a=[1,2,3]
b=[]
a.forEach(i=>b.Push(i))
a.Push(4)
alert(b);

=> 1, 2, 3

Si vous avez besoin d'une copie, utilisez l'opérateur spread, c'est rapide pour cela. Ou utilisez concat comme indiqué par @ftor. Sinon, utilisez Push. Gardez à l'esprit, cependant, il existe des contextes dans lesquels vous ne pouvez pas muter. De plus, avec l'une de ces fonctions, vous obtiendrez une copie superficielle, pas une copie complète. Pour une copie en profondeur, vous aurez besoin de lodash. Lisez plus ici: https://slemgrim.com/mutate-or-not-to-mutate/

0
Rik

Pour ajouter à un grand tableau, l'opérateur spread est énormément plus rapide . Je ne sais pas comment @ftor@Liau Jian Jie A TIRÉ SES CONCLUSIONS, VOIRE DE MAUVAIS TESTS.

 TEST RESULTS  Chrome 71.0.3578.80 (Official Build) (64-bit), FF 63.0.3 (64-bit), & Edge 42.17134.1.0

CELA A DU SENS PUISQUE concat() FAIT UNE COPIE DU TABLEAU ET N'ESSAYE MÊME PAS D'UTILISER LA MÊME MÉMOIRE.

LE PROBLÈME DES "MUTATIONS" NE SEMBLE ÊTRE BASÉ SUR RIEN; SI VOUS ÉCRASEZ VOTRE ANCIEN TABLEAU, concat() N'APPORTE AUCUN AVANTAGE.

LA SEULE RAISON DE NE PAS UTILISER ... SERAIT LE DÉBORDEMENT DE PILE, JE SUIS D'ACCORD AVEC LES AUTRES RÉPONSES QUE VOUS NE POUVEZ PAS UTILISER ... OU apply.
MAIS MÊME DANS CE CAS, UTILISER for {Push()} EST PLUS OU MOINS _/DEUX FOIS PLUS RAPIDE QUE concat()
dans tous les navigateurs et ne débordera pas.
Il n’ya aucune raison d’utiliser concat() sauf si vous avez besoin de pour conserver l’ancien tableau}.

0
Hashbrown