0

I'm having an issue with a JS object where a property value is being rewritten unexpectedly.

In the below example, after I set css_on['color'] = 'red';, outputting css_on to the console shows the correct value. However, after css_off['color'] = 'blue';, for some reason css_on.color is now also blue.

Can someone please tell me why this is happening? And how to stop it! Thanks.

var css = {
    'line-height':  this.options.height+'px',
    'width':        this.options.label_width+'px'
}
var css_on = css
var css_off = css;

css_on['color'] = 'red';
console.log(css_on);
css_off['color'] = 'blue';
console.log(css_on);
David Gard
  • 11,225
  • 36
  • 115
  • 227

4 Answers4

2

Both css_on and css_off are pointing to the same object. When you change a property of that object whether you're using css_on or css_off to refer to it, the change is obviously visible in both ways of accessing it.

If you want two new objects you can clone the original css object. If you don't have a clone function in your prefered library (or don't have a library), you can do this simple thing:

var css_on = Object.create(css);
var css_off = Object.create(css);

This will create two empty objects which have css in their prototype chain. When you do:

console.log(css_on['width']) 

it will look for the width property on the css_on object. It will NOT find it (because css_on is empty), so it will look for it in the prototype chain. It will find it on the css object, so it will log this.options.label_width+'px'

When you do

css_on['width'] = '3px';

You will set the width property of the css_on object. The css object or the css_off objects are not be affected, so it exactly what you want.

Tibos
  • 27,507
  • 4
  • 50
  • 64
  • Thank you for the explanation, it is very useful. Someone suggested in a comment on my question that `css_on = jQuery.extend({}, css);` will also do the job. Is this exactly the same as your method, or will it actually create a brand new object? – David Gard Nov 08 '13 at 09:39
  • jQuery.extend is the clone function in your prefered library. It actually goes through all the properties in css and adds them manually to the resulting object. It doesn't use the prototype chain method. There are advantages and disadvantages to using it, but nothing that should bother you at this point. – Tibos Nov 08 '13 at 09:43
1

This is because both css_on & css_off refer to the same javascript object css. They do not have a copy of their own. So whenever you change any property of one of them, it will reflect on the other.

harsh
  • 1,471
  • 13
  • 12
1

This isn't unexpected behavior, it's what you should be expecting from an object. There are many ways to handle it, though and a simple one is https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

e.g.

var css = {
'line-height':  this.options.height+'px',
'width':        this.options.label_width+'px'
 }

var css_on = Object.create(css);
var css_off = Object.create(css);

This isn't supported in all older browsers though. So if you need support for IE8 or older this will not work. This is a really informational thread that will give you a lot of options, including some more verbose ones (but which are supported in older browsers): How do I correctly clone a JavaScript object?

Community
  • 1
  • 1
spacebean
  • 1,554
  • 1
  • 10
  • 13
1

Because css_on and css_off are both refer to the same object, so you must create an new object for css_on and css_off:

var css_on = new Object(css);
var css_off = new Object(css);

and now, they are different and you can change any one of them.

css_on['width'] = '0px';
css_off['width'] = '2px'
console.log(css_on['width'], css_off['width']); // out put: '0px, 2px'
wiky
  • 6,178
  • 3
  • 16
  • 10