How Lodash _.assign Overwrites Object Properties

In the Lodash JavaScript library, _.assign merges the own enumerable string keyed properties of one or more source objects directly into a destination target object. When resolving key collisions, _.assign strictly overwrites existing properties on the target using a left-to-right evaluation order, meaning the last source object containing a conflicting key wins. This article explains the exact mechanics of how _.assign handles property overwriting, its shallow copying behavior, and how it differs from deep merging.

The Basic Overwrite Mechanism

The syntax for the function is:

_.assign(object, [sources])

The first argument is the target object, and every subsequent argument is a source object. When _.assign is called, it iterates over each source object from left to right. For every own enumerable property found in a source:

  1. It checks the key name.
  2. It assigns the value of that key directly to the target object.
  3. If the target object (or a previous source object) already has a property with the same key, the existing value is immediately replaced with the new value.

Left-to-Right Evaluation

Because properties are assigned in the order the sources appear, subsequent arguments take precedence over prior ones.

const target = { a: 1, b: 2 };
const source1 = { b: 3, c: 4 };
const source2 = { b: 5, d: 6 };

_.assign(target, source1, source2);

console.log(target);
// Output: { a: 1, b: 5, c: 4, d: 6 }

In this example:

Shallow Assignment vs. Recursive Merging

The overwriting process in _.assign is strictly shallow. It uses standard JavaScript assignment semantics under the hood (similar to Object.assign()).

If a property on both the target and the source is an object, _.assign does not inspect or merge the nested properties inside. Instead, it replaces the entire object reference with the one provided by the source.

const target = {
  user: {
    name: 'Alice',
    role: 'Admin'
  }
};

const source = {
  user: {
    name: 'Bob'
  }
};

_.assign(target, source);

console.log(target);
// Output: { user: { name: 'Bob' } }
// Notice that 'role: Admin' was completely erased, not preserved.

If you need nested objects to merge rather than be completely replaced, use Lodash's _.merge instead of _.assign.

Handling of Undefined and Null Values

_.assign treats undefined as a valid value to overwrite existing data. If a source object explicitly defines a key as undefined, it will overwrite the target's existing value with undefined.

const target = { status: 'active' };
const source = { status: undefined };

_.assign(target, source);

console.log(target);
// Output: { status: undefined }

If you want to skip overwriting values when the source value is undefined, Lodash provides _.assignWith or _.defaults, which only assign values if the destination property resolves to undefined.

Target Mutation

A critical aspect of how _.assign works is that it mutates the target object in place and returns that same reference. If you want to create a new object without altering the original target, pass an empty object literal as the first argument:

const original = { a: 1 };
const update = { a: 2 };

const result = _.assign({}, original, update);

console.log(original.a); // 1 (unchanged)
console.log(result.a);   // 2