Object.assign()
Merging, and the source of two persistent misunderstandings: the first argument is changed, and nested objects are shared rather than copied.
Demo
Disjoint keys merge and a repeated key takes the source value — straightforward. The third case is the one that catches people: assign is SHALLOW, so the nested object is not merged property by property. The whole of n is replaced by the source version, and the y that existed only in the target is gone. The last case shows there is no special treatment for null values either — null is a value like any other and overwrites what was there, which is why assign is a poor fit for options merging where absent means keep the default.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| target | object | yes | The object that receives the properties and is MODIFIED. It is also what gets returned. |
| ...sources | object | no (none) | Objects to copy from, left to right — later sources win. Only own enumerable properties are copied, including symbol keys. null and undefined sources are skipped silently. |
Return value
object — The TARGET object itself, modified in place — not a new object. Later sources overwrite earlier ones.
Common patterns
const config = Object.assign({}, defaults, options);
const config = {...defaults, ...options};
Object.assign(Object.create(Object.getPrototypeOf(src)), src);
Examples
Pitfalls
const d = {a: 1}; Object.assign(d, {a: 9}); d
const d = {a: 1}; const merged = Object.assign({}, d, {a: 9}); d
const s = {n: {x: 1}}; const c = Object.assign({}, s); c.n.x = 9; s.n.x
const c = structuredClone(s); c.n.x = 9; s.n.x
const t = {set a(v) { throw new Error("no"); }}; Object.assign(t, {a: 1})
Object.defineProperties(t, Object.getOwnPropertyDescriptors(src))
const s = {get now() { return Date.now(); }}; Object.assign({}, s).now
Object.defineProperties({}, Object.getOwnPropertyDescriptors(s))
When to use
- Merging several sources into one new object
- Deliberately updating an existing object in place
- Copying while preserving a prototype, with Object.create
- Copying symbol-keyed properties, which JSON round trips lose
- You just want a merged literal → spread, which cannot mutate by accident
- Nested data must be independent → structuredClone
- Absent should mean "keep the default" → filter undefined first
- You need getters and setters preserved → getOwnPropertyDescriptors
Notes
FAQ
Spread for building a new object — {...a, ...b} cannot accidentally mutate anything and reads better. Object.assign when you genuinely want to write into an existing object, or when you need symbol keys and a preserved prototype, which object spread also handles for symbols but flattens the prototype.
const merged = {...defaults, ...options}; // new object Object.assign(existing, patch); // deliberate mutation