4

Why does the following snippet return { a: 3, b: undefined } and not { a: 3, b: 2 }?

console.log(Object.assign({ a: 1, b: 2 }, { a: 3, b: undefined }));

This question asks about a function that gives the latter output instead of the former, but my question is why was Object.assign() designed this way? Or to put it a different way, what exactly are the differences between { a: 3 } and { a: 3, b: undefined }?

UPDATE (from the comments on apsillers answer):

{ a: 1 } says "I have no property named 'b'", { a: 1, b: undefined } says "I have a property 'b' but it has not yet been given a value", and { a: 1, b: null } says "I have a property 'b' that should hold an object but has not yet been given an object to hold". Since in the latter two the object has a property 'b', regardless of what the value is, it will still override non-null non-undefined values when passed into Object.assign().

Dominus.Vobiscum
  • 702
  • 1
  • 9
  • 16
  • `{a: 3}` does not have a property called "b" at all, while `{a: 3, b: undefined}` does. – Pointy Feb 26 '19 at 19:56
  • in the second case, you have a value. – Nina Scholz Feb 26 '19 at 19:56
  • 6
    It's the _presence_ of a property that determines whether it's copied, not the value. – Pointy Feb 26 '19 at 19:57
  • 2
    Slightly related: [Why an object where all its properties are undefined is not listed as empty?](https://stackoverflow.com/q/54520607/691711). `undefined` is a value and shouldn't just be ignored. As to ***why*** it was designed this way, I think that may be too broad because it asked for intentions behind a design. – zero298 Feb 26 '19 at 20:00
  • An excellent question with a code snippet demonstrating the outcome. – bvj Jul 30 '19 at 19:32

1 Answers1

5

{ a: 3 } has one property, whose key is the string "a". You can observe this by Object.keys({a:3}), which returns ["a"].

{ a: 3, b: undefined } has two properties, one called a and another called b. Calling Object.keys({a: 3, b: undefined}) returns ["a", "b"].

Object.assign uses values from whatever (enumerable) properties exist on each incoming object, and assign's specification does not handle specially the case where a property happens to have the value undefined.

apsillers
  • 112,806
  • 17
  • 235
  • 239
  • It sort of makes sense that `Object.keys({ a: 1 })` returns `["a"]` whereas `Object.keys({ a: 1, b: undefined })` returns `["a", "b"]`, but it also sort of doesn't. I thought JavaScript maintained `undefined` separately from `null` specifically because `null` communicates "this property exists and has no value" whereas `undefined` communicates "this property does not exist". Hence why `{ a: 1 }.completelyMadeUpPropertyName` returns `undefined`. – Dominus.Vobiscum Feb 27 '19 at 20:37
  • @Dominus `undefined` signifies the lack of a value (but not necessarily the absence of a property, just as we may have defined variables with the value `undefined` so too may we have properties that hold `undefined`) whereas `null` signifies "the intentional absence of any object value" according to the ES5 spec; i.e., `null` specifically indicates an object context/value, but the santics of having no actual value. `null` is, overall, a really dumb feature of the language. – apsillers Feb 27 '19 at 21:42
  • Ah, that makes sense. So `{ a: 1 }` says "I have no property named 'b'", `{ a: 1, b: undefined }` says "I have a property 'b' but it has not yet been given a value", and `{ a: 1, b: null }` says "I have a property 'b' that has intentionally been given no value". Does that sound right? – Dominus.Vobiscum Feb 28 '19 at 20:52
  • @Dominus.Vobiscum Strictly according to the spec, `null` means something like "this should hold an object, and the current value is no-object" but otherwise, that's a pretty good summary. – apsillers Feb 28 '19 at 23:13
  • Ah, okay. Thanks for answering a somewhat esoteric question! – Dominus.Vobiscum Mar 01 '19 at 18:02