How Object.assign Handles Getters and Setters

When working with Object.assign() in JavaScript, accessor properties (getters and setters) are not copied as descriptor functions; instead, they are evaluated during the copy process. Because Object.assign() uses standard [[Get]] and [[Set]] internal methods, it executes the getter on the source object to retrieve a static value, and then assigns that evaluated value to the target object. It does not transfer the underlying getter or setter logic to the destination object.

The Getter Mechanism on Source Objects

When a source object contains a getter property, Object.assign() reads the property value. This read operation invokes the getter function immediately at the time of assignment.

const source = {
  get time() {
    return new Date().toISOString();
  }
};

const target = Object.assign({}, source);

console.log(target.time); // Outputs the timestamp generated during the assign call
// target.time is now a static string, not a getter function

In the example above, target.time becomes a regular data property holding the string returned by the getter, rather than remaining a dynamic property.

The Setter Mechanism on Target Objects

When assigning a value to the target object, Object.assign() triggers any existing setter defined on that target.

const target = {
  set data(value) {
    this._data = value.toUpperCase();
  }
};

const source = { data: 'hello' };

Object.assign(target, source);

console.log(target._data); // "HELLO"

If the target object does not have an existing setter for that property key, Object.assign() simply creates a standard, writable, enumerable, and configurable data property.

Preserving Accessor Descriptors

To properly copy property descriptors—including getters, setters, enumerability, and writability—without evaluating them, use Object.getOwnPropertyDescriptors() in combination with Object.defineProperties().

const source = {
  get dynamicValue() {
    return Math.random();
  }
};

// Preserves the getter function on the new object
const target = Object.defineProperties(
  {},
  Object.getOwnPropertyDescriptors(source)
);

console.log(target.dynamicValue !== target.dynamicValue); // true (getter executes on each access)

By using Object.defineProperties(), the property definition is cloned directly, allowing the target object to retain the original accessor behavior.