0

I stumbled upon a very weird behavior inside a javascript closure. If a "global" variable of a closure is a object, any assignment made from it to another variable behaves like some kind of pointer. A simple code should explain better what I mean:

function closure() {      
  var foo = {key1 : 'value 1'};
  return {
    method: function(){
      var bar = foo;
      bar.key2 = 'value2';
      return 'foo has attributes : ' + foo.key1 + ' and ' + foo.key2
    }
  }
}  
var someVar = closure();
someVar.method();  // returns "foo has attributes : value 1 and value2" (!!!)

I would obviously expect to obtain foo has attributes : value 1 and undefined as only bar has been modified... However, the script actually modifies both bar and foo with confuses me a lot.

I noticed as well that when foo and bar are strings or numbers everything works as expected - modifying bar does not affect foo.

function closure() {
  var foo = 10;
  return {
    method: function(){
      var bar = foo; 
      bar += 1;
      return 'foo is : ' + foo + ' and bar is ' + bar
    }
  }
}
var someVar = closure();
someVar.method(); // returns "foo is : 10 and bar is 11"

I spent whole evening trying to figure out the reason of this behavior... no luck. I got the same results is SpiderMonkey and V8 (both on Chrome and node.js).

What I want to do is of course to modify bar from the first example without affecting foo. Any help will be very appreciated.

vyegorov
  • 21,787
  • 7
  • 59
  • 73
lukaszfiszer
  • 2,591
  • 1
  • 19
  • 13

2 Answers2

2

When you do var bar = foo;, both bar and foo are pointing to the same object. If you don't want foo to change, you need to clone foo. When you clone, you will get an entirely new object, and bar will point to that. This answer has some information regarding the cloning of objects in Javascript.

Also, this behavior isn't limited to a closure. It will happen in regular Javascript code:

var foo = {a: 10, b: 6};
var bar = foo; 
bar.c = 7;
console.log(foo);

Will show foo as having the properties a, b, and c.

What you're seeing is standard behavior of most object-oriented languages. When you are dealing with objects, you are dealing with references to those objects instead of the specific instance-values. You don't see this with numbers because in Javascript they are primitive types, so you're dealing with the actual values rather than a reference to those values.

Community
  • 1
  • 1
Vivin Paliath
  • 94,126
  • 40
  • 223
  • 295
2

This is perfectly normal, expected behavior of almost any object-oriented language, certainly all the dynamic ones I can think of. A variable holds a reference to the object, not the object itself. It's like a pointer, but abstracted away from the details of memory locations; you can't do arithmetic on it or anything. As far as using it, it looks like the variable holds the object directly, but it doesn't. If you pass an object into a function, even though JavaScript is pass-by-value, that function can modify the object. And if you assign it to another variable, you don't get a copy, but an alias: two variables pointing to the same object.

If you want to make a copy, you have to do so explicitly. There is no built-in way to do it automatically, but see this question for some options.

Community
  • 1
  • 1
Mark Reed
  • 91,912
  • 16
  • 138
  • 175