3

I have an object named result which is composed of two objects like :

const a = {bar: {baz: 2}};
var b = {foo: 1};
var result = Object.assign({}, a, b);
console.log(result, a, b);
// result -> {bar: {baz: 2}, foo: 1}
// a -> {bar: {baz: 2}}
// b -> {foo: 1}

Now, I am changing the bar property of the result object like:

result.bar.baz = 3;
result.foo = 4;
console.log(result, a, b);
// result -> {bar: {baz: 3}, foo: 4}
// a -> {bar: {baz: 3}} intresting part
// b -> {foo: 1} intresting, too!

(You can copy and paste code to javascript console in order to see the result for both cases by the way)

There are two weird things here. First one is that I am changing the resulting object's property, but constant object a's property changes, too. Even if first one is the case with Object.assign function, how can I change the constant variable? Let's say this is the case despite const variable mutation, then why the change in property foo does not reflect to the object b? I came with that because I generally use Object.assign to copy objects, but this is pretty weird issue with that function I guess. Any ideas about the case? Thank you.

erdysson
  • 1,450
  • 13
  • 17

2 Answers2

1

Declaring a variable with const only prevents it from being changed to another value. It doesn't prevent the data referenced by that value to change.

const foo = {prop: 'bar'};
foo.prop = 'baz'; // Works well
foo = 'baz'; // TypeError: invalid assignment to const `foo'

If you want to prevent an object from being changed, you can freeze it.

Object.freeze(foo);
foo.prop = 'buz'; // TypeError: "prop" is read-only

However, that will only affect own properties. If the value of one of these is another object, it won't become frozen.

This is what happens with Object.assign too. It only copies own properties, and if their value is an object, it won't "clone" it. That is, the references will still be the same, and changes will be reflected.

If you want to deeply clone an object, see What is the most efficient way to clone an object?

Community
  • 1
  • 1
Oriol
  • 274,082
  • 63
  • 437
  • 513
0

Object.assign will work but with the added gotcha that if any of the properties you are assigning from contain an object as a value it does not create a copy of that object, so the references do not change, the property in the created object will point to that same nested object.

Also constant in javascript can be deceiving, you can add and remove properties from a 'const' object as long as you don't try to reassign it to a new object or a different primitive.

Same will occur with arrays, you can create a 'const' array but push pop off of it.

https://jsfiddle.net/eu9yg37s/6/ just something I was messing around in to attempt to display what I mean.

const a = {bar: {baz: 2}};
var b = {foo: 1};

// Example of customizer function that may take into account nested objects and properly copy them using lodash 4.x
var customizer = function(objValue, srcValue) {
  if (typeof srcValue === 'object' && typeof srcValue !== null) {
    return _.assignWith({}, srcValue, customizer);
  }
  return _.isUndefined(objValue) ? srcValue : objValue;
}

// This calls assign, but will invoke the customizer function
var result = _.assignWith({}, a, b, customizer);

result.bar.baz = 3;
result.foo = 4;
console.log(result, a, b);

// 'Constant' Array Example
const hi = [true, false, 'hi'];

hi[2] = 23;
console.log('hi.pop', hi.pop());
console.log('hi', hi);

// These will error, uncomment out to see it errors on any of these attempts
//hi = 3;
//hi = 'no';
hi = [true, false, 23];
//hi = false;
//hi = {};

The change doesn't reflect in b, because it wasn't a nested object during the assign operation so the property foo in our created object is pointing to a new primitive 1

Rob Soule
  • 66
  • 4