Andrew DeWitt Andrew DeWitt - 4 months ago 13
Javascript Question

side affecting new object is changing the original object

Expected results



I've written a function to take a simple object literal, and reformat it as seen in this example:

var original = { a: 1, b: true, c: "a string" }

// run through function ...

var result = {
a: { type: "int", val: 1 },
b: { type: "bit", val: 1 },
c: { type: "varchar(250)", val: "a string" }
}


Current Solution



The function works, using a
for ... in
loop to iterate over the original object & build a new object based on the original data.

function change (data) {

function loop (d) {

var orig = d;
var end = d;

for (var key in orig) {

var property = {
type: "test",
val: orig[key]
};

end[key] = property;
}

return end;
}

// create object using loop & return
var p = loop(data);
return p;

}


Actual results



However, this function is also changing the original object passed in & I can't seem to figure out why.

var original = { a: 1, b: true, c: "a string" }

// run through function ...
var result = change(original);

console.log(result); // => {
// a: { type: "int", val: 1 },
// b: { type: "bit", val: 1 },
// c: { type: "varchar(250)", val: "a string" }
// }

console.log(original); // => {
// a: { type: "int", val: 1 },
// b: { type: "bit", val: 1 },
// c: { type: "varchar(250)", val: "a string" }
// }
// instead of expected:
// { a:1, b:true, c:"a string" }


I'm guessing that it has something to do with my not fully understanding scope in javascript & possibly something about prototypical inheritance (which I definitely need to learn more about), but at this point I'm just not sure where to begin to really understand what's happening here. And I'm just extra confused when I compare it to my simple understanding of side-affecting in JavaScript:

var a = 1;
var b = a;
console.log(a) // => 1
console.log(b) // => 1

b = 2
console.log(a) // => 1
console.log(b) // => 2


The function works as is and is doing what I need it to in the rest of my program, but this issue is really bothering me since I don't understand what's going on here.

Answer

As you can read in the Docs in Javascript when an object is passed as an argument if you decide to mutate it in the function that will be a reference to the original object and will mutate it too.

If you just handle it like read only it would be just as a regular argument ( passed by value )

For Objects that are flat ( do not contain nested objects ) you can use Object.assign (Es6 feature with available polyfill)

let obj1 = { a: 1};
let obj2 = Object.assign({}, obj1);

For more complex objects i suggest you use some library method like lodash's _.cloneDeep

// you have to include lodash.js in your page
let obj1 = { a: 1};
let obj2 = _.cloneDeep(obj1);

or jQuery's $.extend

// you have to include jquery.js in your page
let obj1 = { a: 1};
let obj2 = $.extend(true, {}, obj1);
Comments