web-dev-qa-db-fra.com

Object spread vs. Object.assign

Disons que j’ai une variable options et que je veux définir une valeur par défaut.

Quels sont les avantages/inconvénients de ces deux alternatives?

Utilisation de la propagation d'objet

options = {...optionsDefault, ...options};

Ou en utilisant Object.assign

options = Object.assign({}, optionsDefault, options);

C'est le commit qui m'a fait me demander.

317

Ce n'est pas nécessairement exhaustif.

Syntaxe Spread

options = {...optionsDefault, ...options};

Avantages:

  • Si vous créez du code pour une exécution dans des environnements sans support natif, vous pourrez peut-être simplement compiler cette syntaxe (au lieu d'utiliser un polyfill). (Avec Babel, par exemple.)

  • Moins verbeux.

Désavantages:

  • Lorsque cette réponse a été écrite à l'origine, il s'agissait d'une proposition , non normalisée. Lorsque vous utilisez des propositions, tenez compte de ce que vous feriez si vous écriviez du code avec celui-ci maintenant et qu'il ne soit pas normalisé ou ne change pas à mesure qu'il avance vers la normalisation. Cela a depuis été normalisé dans ES2018.

  • Littéral, pas dynamique.


Object.assign()

options = Object.assign({}, optionsDefault, options);

Avantages:

  • Normalisé.

  • Dynamique. Exemple:

    var sources = [{a: "A"}, {b: "B"}, {c: "C"}];
    options = Object.assign.apply(Object, [{}].concat(sources));
    // or
    options = Object.assign({}, ...sources);
    

Désavantages:

  • Plus verbeux.
  • Si vous créez du code pour une exécution dans des environnements sans support natif, vous devez effectuer un polyfill.

C'est le commit qui m'a fait me demander.

Ce n'est pas directement lié à ce que vous demandez. Ce code n'utilisait pas Object.assign(), il utilisait un code utilisateur (object-assign) qui fait la même chose. Ils semblent compiler ce code avec Babel (et le regrouper avec Webpack), ce dont je parlais: la syntaxe que vous pouvez simplement compiler. Ils préféraient apparemment cela d’avoir à inclure object-assign en tant que dépendance qui entrerait dans leur construction.

282
JMM

Pour les objets de référence, repos/propagation est finalisé dans ECMAScript 2018 en tant qu’étape 4. La proposition peut être trouvée ici .

Dans la plupart des cas, la réinitialisation et la propagation d'un objet fonctionnent de la même manière, la principale différence est que la propagation définit les propriétés, alors que Object.assign () les définit . Cela signifie que Object.assign () déclenche les setters.

Il est utile de rappeler que, mis à part cela, object rest/spread 1: 1 est mappé sur Object.assign () et agit différemment par array (iterable). Par exemple, lors de la propagation d'un tableau, les valeurs NULL sont étendues. Cependant, en utilisant la propagation d'objet, les valeurs nulles sont étendues silencieusement.

Exemple de propagation d'un tableau (Iterable)

const x = [1, 2, null , 3];
const y = [...x, 4, 5];

console.log(y); // [1, 2, null, 3, 4, 5];

Exemple de propagation d'objet

const x = null;
const y = {a: 1, b: 2};
const z = {...x, ...y};

console.log(z); //{a: 1, b: 2}

Ceci est cohérent avec la manière dont Object.assign () fonctionnerait, les deux excluant silencieusement la valeur null sans erreur.

const x = null;
const y = {a: 1, b: 2};
const z = Object.assign({}, x, y);

console.log(z); //{a: 1, b: 2}
138
tomhughes

Je pense qu’une grande différence entre l’opérateur de propagation et le Object.assign qui ne semble pas être mentionné dans les réponses actuelles est que l’opérateur de propagation ne gardera pas le prototype intact. Si vous souhaitez ajouter des propriétés à un objet et que vous ne voulez pas en changer l'instance, vous devrez utiliser Object.assign. L'exemple ci-dessous devrait démontrer ceci:

const error = new Error();
error instanceof Error // true

const errorExtendedUsingSpread = {
  ...error,
  ...{
    someValue: true
  }
};
errorExtendedUsingSpread instanceof Error; // false

const errorExtendedUsingAssign = Object.assign(error, {
  someValue: true
});
errorExtendedUsingAssign instanceof Error; // true
20
Sean Dawson

L'opérateur d'étalement d'objet (...) ne fonctionne pas dans les navigateurs, car il ne fait pas encore partie d'une spécification ES, il s'agit simplement d'une proposition. La seule option est de le compiler avec Babel (ou quelque chose de similaire).

Comme vous pouvez le constater, il n’ya que le sucre syntaxique sur Object.assign ({}).

Autant que je sache, ce sont les différences importantes.

  • Object.assign fonctionne dans la plupart des navigateurs (sans compilation)
  • ... pour les objets n'est pas normalisé
  • ... vous protège de la mutation accidentelle de l'objet
  • ... va polyfill Object.assign dans les navigateurs sans lui
  • ... a besoin de moins de code pour exprimer la même idée
12
Karthick Kumar

Comme d'autres l'ont mentionné, à ce moment-ci, Object.assign() nécessite un polyfill et la propagation d'objet ... nécessite un transpiling (et peut-être aussi un polyfill) pour fonctionner.

Considérez ce code:

// Babel wont touch this really, it will simply fail if Object.assign() is not supported in browser.
const objAss = { message: 'Hello you!' };
const newObjAss = Object.assign(objAss, { dev: true });
console.log(newObjAss);

// Babel will transpile with use to a helper function that first attempts to use Object.assign() and then falls back.
const objSpread = { message: 'Hello you!' };
const newObjSpread = {...objSpread, dev: true };
console.log(newObjSpread);

Ces deux produisent le même résultat.

Voici la sortie de Babel vers ES5:

var objAss = { message: 'Hello you!' };
var newObjAss = Object.assign(objAss, { dev: true });
console.log(newObjAss);

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var objSpread = { message: 'Hello you!' };
var newObjSpread = _extends({}, objSpread, { dev: true });
console.log(newObjSpread);

C'est ce que j'ai compris jusqu'à présent. Object.assign() est en fait normalisé, alors que l'objet spread ... ne l'est pas encore. Le seul problème est la prise en charge par le navigateur pour le premier et à l'avenir, le second aussi.

Jouez avec le code ici

J'espère que cela t'aides.

12

Je voudrais résumer l’état de la fonctionnalité ES "de fusion d’objets dispersés", dans les navigateurs et dans l’écosystème via des outils.

Spec

Navigateurs: sous Chrome, sous SF, bientôt sous Firefox (version 60, IIUC)

  • Prise en charge du navigateur pour les "propriétés de propagation" fourni avec Chrome 60 , y compris ce scénario.
  • La prise en charge de ce scénario ne fonctionne PAS dans Firefox actuel (59), mais fonctionne dans mon Firefox Developer Edition. Donc, je crois que ce sera expédié dans Firefox 60.
  • Safari: non testé, mais Kangax indique que cela fonctionne dans Desktop Safari 11.1, mais pas dans SF 11
  • iOS Safari: pas testé, mais Kangax dit que cela fonctionne dans iOS 11.3, mais pas dans iOS 11
  • pas encore dans Edge

Outils: Node 8.7, TS 2.1

Liens

Exemple de code (sert également de test de compatibilité)

var x = { a: 1, b: 2 };
var y = { c: 3, d: 4, a: 5 };
var z = {...x, ...y};
console.log(z); // { a: 5, b: 2, c: 3, d: 4 }

Encore une fois: au moment de l'écriture, cet exemple fonctionne sans transpilation dans Chrome (60+), Firefox Developer Edition (aperçu de Firefox 60) et Node (8.7+).

Pourquoi répondre?

J'écris ces 2,5 années après la question initiale. Mais j'avais la même question, et c'est là que Google m'a envoyé. Je suis un esclave de la mission de SO: améliorer la longue queue.

Comme il s’agit d’une extension de la syntaxe "array spread", j’ai trouvé très difficile de rechercher sur Google et difficile à trouver dans les tableaux de compatibilité. Le plus proche que j'ai pu trouver est Kangax "property spread" , mais ce test n'a pas deux spreads dans la même expression (pas une fusion). En outre, le nom figurant dans les pages de propositions/brouillons/statut du navigateur utilise tous les termes "property spread", mais il me semble que c'était un "premier principe" auquel la communauté est parvenue après les propositions d'utilisation de la syntaxe spread pour "fusion d'objets". (Ce qui pourrait expliquer pourquoi il est si difficile de chercher sur Google.) C'est pourquoi je documente mes conclusions ici afin que les autres utilisateurs puissent afficher, mettre à jour et compiler des liens concernant cette fonctionnalité spécifique. J'espère qu'il comprend. Aidez-nous à répandre la nouvelle de l'atterrissage dans les spécifications et dans les navigateurs.

Enfin, j'aurais ajouté cette information sous forme de commentaire, mais je ne pouvais pas la modifier sans enfreindre l'intention originale de l'auteur. Plus précisément, je ne peux pas éditer le commentaire de @ ChillyPenguin sans perdre son intention de corriger @RichardSchulte. Mais des années plus tard, Richard a eu raison (à mon avis). J'écris donc cette réponse à la place, en espérant qu'elle finira par avoir une influence sur les anciennes réponses (cela pourrait prendre des années, mais c'est en quoi consiste l'effet à longue traîne , après tout).

11
yzorg

REMARQUE: Spread n'est PAS simplement un sucre syntaxique autour de Object.assign. Ils fonctionnent beaucoup différemment dans les coulisses.

Object.assign applique des paramètres à un nouvel objet, mais pas Spread. De plus, l'objet doit être itérable.

Copier Utilisez ceci si vous avez besoin de la valeur de l'objet telle qu'elle est en ce moment et si vous ne voulez pas que cette valeur reflète les modifications apportées par les autres propriétaires de l'objet.

Utilisez-le pour créer une copie superficielle de l'objet. Il est conseillé de toujours définir les propriétés immuables sur copie. Les versions mutables pouvant être passées en propriétés immuables, copy garantit que vous aurez toujours affaire à un objet immuable.

Assign Assign est un peu le contraire de copier. Assign générera un setter qui affecte directement la valeur à la variable d'instance, au lieu de la copier ou de la conserver. Lors de l'appel du getter d'une propriété assign, il renvoie une référence aux données réelles.

8
Charles Owen

Les autres réponses sont anciennes, on ne pouvait pas avoir une bonne réponse.
L'exemple ci-dessous concerne les littéraux d'objet, explique comment les deux peuvent se compléter et comment ils ne peuvent pas se compléter (donc la différence):

var obj1 = { a: 1,  b: { b1: 1, b2: 'b2value', b3: 'b3value' } };

// overwrite parts of b key
var obj2 = {
      b: {
        ...obj1.b,
        b1: 2
      }
};
var res2 = Object.assign({}, obj1, obj2); // b2,b3 keys still exist
document.write('res2: ', JSON.stringify (res2), '<br>');
// Output:
// res2: {"a":1,"b":{"b1":2,"b2":"b2value","b3":"b3value"}}  // NOTE: b2,b3 still exists

// overwrite whole of b key
var obj3 = {
      b: {
        b1: 2
      }
};
var res3 = Object.assign({}, obj1, obj3); // b2,b3 keys are lost
document.write('res3: ', JSON.stringify (res3), '<br>');
// Output:
  // res3: {"a":1,"b":{"b1":2}}  // NOTE: b2,b3 values are lost

Plusieurs autres petits exemples ici, aussi pour array & object:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

2

Cela fait maintenant partie de ES6, est donc normalisé et est également documenté sur MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator

Il est très pratique à utiliser et a beaucoup de sens en plus de la déstructuration des objets.

Le dernier avantage énuméré ci-dessus concerne les capacités dynamiques de Object.assign (). Toutefois, cela est aussi simple que d'étendre le tableau à l'intérieur d'un objet littéral. Dans la sortie compilée babel, il utilise exactement ce qui est démontré avec Object.assign ()

Donc, la bonne réponse serait d'utiliser object spread puisqu'il est maintenant standardisé, largement utilisé (voir réagir, redux, etc.), qu'il est facile à utiliser et qu'il possède toutes les fonctionnalités de Object.assign ().

0
Rikki Schulte