The Object assign() method

By

Learn how JavaScript Object.assign() mutates a target with enumerable own string and Symbol properties, how conflicts are resolved, and why copies are shallow.

~~~

Object.assign() copies enumerable own properties from one or more source objects to a target object.

It mutates and returns the target:

const car = {
  brand: 'Ford'
}

const result = Object.assign(car, {
  model: 'Fiesta'
})

car //{ brand: 'Ford', model: 'Fiesta' }
result === car //true

Both string and Symbol property keys are copied. Inherited and non-enumerable properties are not.

See the MDN Object.assign() reference and the ECMAScript specification for the complete rules.

Merge objects

Use an empty target when you do not want to mutate any source:

const details = {
  brand: 'Ford',
  model: 'Fiesta'
}

const status = {
  available: true
}

const car = Object.assign({}, details, status)

Later sources overwrite earlier properties with the same key:

const car = Object.assign(
  {},
  { color: 'blue' },
  { color: 'red' }
)

car.color //'red'

This ordering rule is what makes Object.assign() a good fit for applying defaults. Put the defaults first, then the values that should win:

const defaults = {
  method: 'GET',
  timeout: 5000
}

const options = Object.assign({}, defaults, { method: 'POST' })

options //{ method: 'POST', timeout: 5000 }

The classic pitfall here is forgetting the empty target:

const options = Object.assign(defaults, { method: 'POST' })

This works once, but it mutated defaults. Every later call now starts from method: 'POST'. Pass {} as the first argument when the sources must stay untouched.

Today the spread syntax does the same merge without a target: { ...defaults, method: 'POST' }. Reach for Object.assign() when you specifically want to copy properties into an existing object.

Copies are shallow

Object.assign() copies property values. When a value is an object, it copies the reference:

const original = {
  model: 'Fiesta',
  details: {
    color: 'blue'
  }
}

const copied = Object.assign({}, original)

original.model = 'Focus'
original.details.color = 'yellow'

copied.model //'Fiesta'
copied.details.color //'yellow'

The top-level model values are independent. Both objects still reference the same nested details object.

Use structuredClone() when you need a deep copy and your values are supported by that algorithm.

Getters and setters run

Object.assign() reads each source property and writes it to the target.

This means source getters and target setters run. Property descriptors are not copied:

const source = {
  get total() {
    return 42
  }
}

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

Object.getOwnPropertyDescriptor(target, 'total')
//{ value: 42, writable: true, enumerable: true, configurable: true }

The getter ran once during the copy. The target ends up with a plain data property holding whatever the getter returned at that moment.

Use Object.getOwnPropertyDescriptors() with Object.defineProperties() when you need to preserve descriptors.

null and undefined sources are skipped. A null or undefined target throws a TypeError.

Tagged: JavaScript ยท All topics
~~~

Related posts about js: