DEV Community

Alex M
Alex M

Posted on

How to Clone an Object in JavaScript

How to... js series

The way JavaScript handles assignments to copy objects is different from how it handles primitive values. Instead of holding values, it uses a pointer to the value in memory.
This concept is known as assignment by reference, where the variable doesn't store the actual value but a reference to the object's memory location. This implies that if two variables point to the same object, any modification to one of them will affect both.

Trying direct assignment

code

const weather= { today:'🌞'}
const currentWeather = weather
currentWeather.today = '🌧'
Enter fullscreen mode Exit fullscreen mode

test

test('should preserve the value', () => {
 expect(weather.today).toBe('🌞')
})
Enter fullscreen mode Exit fullscreen mode

❌ FAIL should preserve the value

It fails because an object is not a primitive value so in this case JavaScript uses assignment by reference.

Strategies

Depending on the original object and specific needs, one can choose between two copying strategies in JavaScript:

Shallow copy

A shallow copy creates a new object where only the top-level structure of the object is duplicated, while the nested objects or elements inside the original object still maintain their references.

Using spread syntax

code

const weather= {
  today:'🌞',
  forecast: { morning:'🌞'}
}
const currentWeather = { ...weather }
currentWeather.today = '🌧'
currentWeather.forecast.morning = 'β›…'
Enter fullscreen mode Exit fullscreen mode

test

test('should preserve the value', () => {
 expect(weather.today).toBe('🌞')
})

test('should preserve the nested value', () => {
 expect(weather.forecast.morning).toBe('🌞')
})
Enter fullscreen mode Exit fullscreen mode

βœ… PASS should preserve the value
❌ FAIL should preserve the nested value

Using Object.assign()

code

const weather= {
  today:'🌞',
  forecast: { morning:'🌞'}
}
const currentWeather = Object.assign({}, weather)
currentWeather.today = '🌧'
currentWeather.forecast.morning = 'β›…'
Enter fullscreen mode Exit fullscreen mode

test

test('should preserve the value', () => {
 expect(weather.today).toBe('🌞')
})

test('should preserve the nested value', () => {
 expect(weather.forecast.morning).toBe('🌞')
})
Enter fullscreen mode Exit fullscreen mode

βœ… PASS should preserve the value
❌ FAIL should preserve the nested value

Deep copy

In contrast, a deep copy creates independent copies of all nested objects, ensuring that there are no shared references.

Cloning Objects using JSON.parse()/JSON.stringify()

code

const weather= {
  today:'🌞',
  forecast: { morning:'🌞'}
}
const currentWeather = JSON.parse(JSON.stringify(weather))
currentWeather.today = '🌧'
currentWeather.forecast.morning = 'β›…'
Enter fullscreen mode Exit fullscreen mode

test

test('should preserve the value', () => {
 expect(weather.today).toBe('🌞')
})

test('should preserve the nested value', () => {
 expect(weather.forecast.morning).toBe('🌞')
})
Enter fullscreen mode Exit fullscreen mode

βœ… PASS should preserve the value
βœ… PASS should preserve the nested value

⚠️ NOTE: JSON.parse/JSON.stringify method has important limitations:

  • Date is converted to string
  • Infinity and NaN are converted to null
  • undefined, function and Symbol as value in object property are ignored and convert to null in arrays.

Using structuredClone() ❀️

code

const weather= {
  today:'🌞',
  forecast: { morning:'🌞'}
}
const currentWeather = structuredClone(weather)
currentWeather.today = '🌧'
currentWeather.forecast.morning = 'β›…'
Enter fullscreen mode Exit fullscreen mode

test

test('should preserve the value', () => {
 expect(weather.today).toBe('🌞')
})

test('should preserve the nested value', () => {
 expect(weather.forecast.morning).toBe('🌞')
})
Enter fullscreen mode Exit fullscreen mode

βœ… PASS should preserve the value
βœ… PASS should preserve the nested value

Structured cloning offers distinct advantages over JSON.parse()/JSON.stringify(). It excels in managing intricate objects beyond the capabilities of JSON, including objects with binary data or cyclic object graphs.

Nonetheless, structured cloning does come with certain limitations. It is unable to handle prototypes, functions, Symbol, and certain values like Error and DOM nodes. ref

To fully values support in deep copy (functions, Symbol..) is necessary iteration strategy, but in most cases structuredClone() is good enough.

It's important to note that the structuredClone() method is not supported in every browser.

Support for structuredClone:

Top comments (0)