Mysteriously mutating objects

All objects in javaScript are mutable. In our own code base we use this for better or ill with very few train wrecks. But when we start accepting objects from other modules and unexpectedly mutating them, we can get into very hard to debug situations.

// ------- User Code ---------- var options = { type: 'dog' } var Spot = new buildPet(options) console.dir(Spot) var Fido = new buildPet(options) console.dir(Fido) // ------- Our Third Party Module ---------- function buildPet (options) { var color = [ 'black', 'white', 'brownn' ] if (!options) options = {} if (!options.color) { var pick = Math.floor(Math.random() * 3) options.color = color[pick] } for (var i in options) { this[i] = options[i] } }

The user intends to build two new pets. Both dogs, with random colors. However, since we mutate the options object they provided us in order to add computed or default properties the color will always be the same for both Spot and Fido.

The solution is simple, if you don't know how other parts of the code that share a reference to the same object will be using that object, then copy it before you mutate.

var pc = require('photocopy') // ------- User Code ---------- var options = { type: 'dog' } var Spot = new buildPet(options) console.dir(Spot) var Fido = new buildPet(options) console.dir(Fido) // ------- Our Third Party Module ---------- // function buildPet (options) { var color = [ 'black', 'white', 'brownn' ] if (!options) options = {} options = pc(options) if (!options.color) { var pick = Math.floor(Math.random() * 3) options.color = color[pick] } for (var i in options) { this[i] = options[i] } }

Now each dog gets its own randomly assigned color, even though they are sharing the same options object. Defensive copying's only big disadvantage is performance overhead. Though it's better to not mutate when possible.