Dealing With Values by Reference – (a.k.a cloning)



Dealing With Values by Reference – (a.k.a cloning)

0 0


sdjs-cloning

Lightning talk on cloning in JavaScript

On Github shrunyan / sdjs-cloning

Dealing With Values by Reference

(a.k.a cloning)

@stuartrunyan

I constantly forget that in JavaScript values are passed by reference.

This has caused me many pains when dealing with state mutations.

One of my latest issue with this was in momentjs.

let moment = require('moment')
let time = moment()

console.log(time.format("dddd, MMMM Do YYYY, h:mm:ss a"))
// "Tuesday, October 6th 2015, 11:17:32 am"

console.log(time.utc().format("dddd, MMMM Do YYYY, h:mm:ss a"))
// "Tuesday, October 6th 2015, 6:17:32 pm"

console.log(time.format("dddd, MMMM Do YYYY, h:mm:ss a"))
// "Tuesday, October 6th 2015, 6:17:32 pm"

// WAT?

Read the Docs. I mean REALLY read the docs!

Sets a flag on the original moment to internally use Date#getUTC* and Date#setUTC* instead of Date#get* and Date#set*. — Moment UTC

totally my fault

Momentjs is simply the latest example of this problem for me.

I used to deal with this a lot in Backbonejs with complex data structures in models.

How can we avoid this specific issue?

Clone the, moment, object.

let moment = require('moment')
let time = moment()

console.log(time.format("dddd, MMMM Do YYYY, h:mm:ss a"))
// "Tuesday, October 6th 2015, 11:17:32 am"

console.log(time.clone().utc().format("dddd, MMMM Do YYYY, h:mm:ss a"))
// "Tuesday, October 6th 2015, 6:17:32 pm"

console.log(time.format("dddd, MMMM Do YYYY, h:mm:ss a"))
// "Tuesday, October 6th 2015, 11:17:32 am"

Great! But how do I do this outside of moment?

Check your preferred lib it most likely has a clone() function.

$.clone()
_.clone()

Otherwise, I would recommend lodash.clone

lodash is going to be a good framework agnostic solution.

Not all copying/cloning is equal

let me = {
  name: 'Stuart',
  movies: [{
    title: 'Star Wars',
    fav: true
  },{
    title: 'Mad Max 2'
  },{
    title: 'Monty Python'
  }]
}

let miniMe = Object.assign({}, me)
miniMe.name = 'Mini Stuart'
miniMe.movies[0].fav = false

console.log(me)
// {"name":"Stuart","movies":[{"title":"Star Wars","fav":false},{"title":"Mad Max 2"},{"title":"Monty Python"}]}

console.log(miniMe)
// {"name":"Mini Stuart","movies":[{"title":"Star Wars","fav":false},{"title":"Mad Max 2"},{"title":"Monty Python"}]}

An illustration of this problem with a complex data structure.

Explain shallow vs deep clone/copy

A few things to consider.

When dealing with state, think about where it came from and if it's a reference. Even when cloning understand your data structure and if it needs a deep clone. When possible prefer Immutable data structures. This is something you have to enforce in your code.

Alot of times we inherit codebases. Make sure you wrap your head around the codes state.

Complex data structures need deep clones otherwise you introduce the same mutation issue but in a more convoluted way.

JavaScript is not inherently immutable. It's a pattern you must apply to your code. This is out of scope of the talk.

If interested in understanding values by reference and cloning objects you should read up on a few topics.

  • Cloning / Shallow copy
  • Deep cloning
  • Immutable data structures

Can not stress immutable enough. This is what the industry is pushing towards. Espcially with one-way data flows.

Your Friend

_.clone()

in summary

I'd encourage you not to write this logic on your own. The community testing factor of oss solutions can't be underestimated.

Questions?

Dealing With Values by Reference (a.k.a cloning) @stuartrunyan