๐Ÿš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
๐ŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
JS MASTER CLASS /// MASTER THE ENGINE /// BUILD LOGIC /// ASYNC PATTERNS /// JS MASTER CLASS /// MASTER THE ENGINE ///

Object.assign() | JavaScript Tutorial - In-Depth Guide

Master Object.assign(): merging and copying objects, its mutation of the target argument, comparison with spread syntax, and correctly copying property getters/setters.

โšก Total XP: 0|๐Ÿ’ป javascript XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

System Hub

Core logic.

Quick Quiz //

Does `Object.assign(target, source)` mutate the `target` object?


๐Ÿš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
๐ŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

Object.assign() was the original way to merge objects in JavaScript, predating the object spread operator. Professional codebases still encounter it constantly in older code and in a few situations spread cannot cover as cleanly.

1Object.assign() | JavaScript Tutorial - In-Depth Guide Part 1

Object.assign(target, ...sources) copies own enumerable properties from one or more source objects into a target object, returning the target.

โœ•
โ€”
+
const target = { a: 1 };
Object.assign(target, { b: 2 });
target; // { a: 1, b: 2 } โ€” target was mutated!
localhost:3000
๐Ÿงท

Merging into Target

2Object.assign() | JavaScript Tutorial - In-Depth Guide Part 2

To avoid mutating an existing object, pass an empty object literal as the first argument โ€” this is the classic pre-spread idiom for a non-mutating merge.

โœ•
โ€”
+
const merged = Object.assign({}, defaults, overrides);
// equivalent to: { ...defaults, ...overrides }
localhost:3000

Non-Mutating Idiom

3Object.assign() | JavaScript Tutorial - In-Depth Guide Part 3

Like spread, Object.assign() only performs a shallow copy and follows the same left-to-right, later-source-wins overwrite rule.

โœ•
โ€”
+
Object.assign({}, { x: 1 }, { x: 2 }); // { x: 2 }
localhost:3000

Same Shallow Semantics

4Object.assign() | JavaScript Tutorial - In-Depth Guide Part 4

Object.assign() copies a getter's current computed value, not the getter definition itself โ€” the target ends up with a plain data property.

โœ•
โ€”
+
const source = { get random() { return Math.random(); } };
const copy = Object.assign({}, source);
// copy.random is a fixed number, not a live getter
localhost:3000

Getters Become Values

5Object.assign() | JavaScript Tutorial - In-Depth Guide Part 5

Object.assign() is still preferred over spread in a few cases: merging into an existing object on purpose, or merging class instances where spread would lose the prototype.

โœ•
โ€”
+
class Point { distance() { /* ... */ } }
const p = Object.assign(new Point(), { x: 1, y: 2 });
p.distance(); // still works, p is still a Point
localhost:3000

When Assign Still Wins

6Step-by-Step Breakdown

Object.assign(target, ...sources) copies own enumerable properties from one or more source objects into a target object, returning the target.

Checkpoint: Does Object.assign(target, source) mutate the target object?

  • โ†’Yes, target is mutated and also returned
  • โ†’No, it always returns a brand-new object

To avoid mutating an existing object, pass an empty object literal as the first argument โ€” this is the classic pre-spread idiom for a non-mutating merge.

Like spread, Object.assign() only performs a shallow copy and follows the same left-to-right, later-source-wins overwrite rule.

Object.assign() copies a getter's current computed value, not the getter definition itself โ€” the target ends up with a plain data property.

Object.assign() is still preferred over spread in a few cases: merging into an existing object on purpose, or merging class instances where spread would lose the prototype.

Checkpoint: Does spreading a class instance ({ ...instance }) preserve its prototype and methods?

  • โ†’Yes, spread preserves the prototype
  • โ†’No, it produces a plain object without the prototype

Next, we'll explore 'Deep Copy vs Shallow Copy'.

Level Up ๐Ÿš€

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1Be Careful Merging Accessibility Props with Object.assign()

Using Object.assign(defaultProps, userProps) without a fresh target can mutate a shared defaultProps object used by other component instances, silently corrupting their default aria attributes too.

SEO Implications

  • 1

    No Direct SEO Effect

    Object.assign() is a data-manipulation utility; SEO relevance is limited to indirect effects from bugs caused by unintended shared-object mutation.

Best Practices

Prefer Spread for Simple, Non-Mutating Merges

Object spread is more concise and its non-mutating behavior is the default, avoiding the target-mutation footgun that Object.assign() carries.

Reach for Object.assign() When You Specifically Need to Mutate an Existing Object

When the intent genuinely is "add these properties onto this existing object" (e.g. initializing an instance), Object.assign() communicates that intent more directly than a spread-and-reassign.

Frequent Bugs

THE BUG

Calling `Object.assign(defaults, overrides)` intending to create a merged copy, but accidentally mutating the shared `defaults` object referenced elsewhere in the app.

THE FIX

Always pass a fresh empty object literal as the first argument โ€” `Object.assign({}, defaults, overrides)` โ€” when you want a non-mutating merge.

THE BUG

Expecting a getter copied via Object.assign() to remain dynamically computed on the copy, then being confused when it holds a stale, frozen-in-time value.

THE FIX

Remember assign() copies the current value of a getter, not its definition; if a live computed property is needed on the copy, redefine it explicitly with Object.defineProperty.

Real-World Examples

Initializing a Class Instance from a Plain Data Object

A data layer needed to hydrate class instances (with methods) from plain JSON objects returned by an API, without losing the class's prototype.

class User {
  greet() { return `Hi, ${this.name}`; }
}
const user = Object.assign(new User(), jsonFromApi);
user.greet(); // works, user is still a real User instance

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Mutating a shared object by omitting the empty target

const merged = Object.assign({}, sharedDefaults, overrides);

The Solution //

Always pass a new object literal as the first argument when the intent is a non-mutating merge.

Lesson Glossary

[01]Object.assign()

Copies own enumerable properties from source objects onto a target object, mutating and returning it.

Code Preview
Object.assign(t, s)

[02]Mutation

Modifying an object's existing properties in place, as opposed to creating a new object.

Code Preview
target.a = 1

[03]Own Enumerable Property

A property directly on an object (not inherited) that shows up in for...in loops and Object.keys().

Code Preview
Object.keys(obj)

[04]Prototype Preservation

Retaining an object's prototype chain when copying properties, as Object.assign onto an instance does.

Code Preview
Object.assign(new C(), s)

[05]Shallow Merge

Combining objects by copying only their top-level properties.

Code Preview
Object.assign({}, a, b)

Continue Learning