54

I know that in JS, objects are passed by reference, for example:

function test(obj) {
    obj.name = 'new name';
}

var my_obj = { name: 'foo' };
test(my_obj);
alert(my_obj.name); // new name

But why doesn't the below work:

function test(obj) {
    obj = {};
}

var my_obj = { name: 'foo' };
test(my_obj);
alert(my_obj.name); // foo

I have set the object to {} (empty) but it still says foo.

Can any one explain the logic behind this?

tckmn
  • 57,719
  • 27
  • 114
  • 156
Dev555
  • 2,128
  • 4
  • 30
  • 40

4 Answers4

67

If you are familiar with pointers, that's an analogy you can take. You're actually passing a pointer, so obj.someProperty would dereference to that property and actually override that, while merely overriding obj would kill off the pointer and not overwrite the object.

Alexander Varwijk
  • 2,075
  • 18
  • 21
  • 2
    +1 - nicely done, and clearer than my answer (assuming OP knows about pointers) – Adam Rackis Feb 24 '12 at 21:13
  • @AdamRackis I +1'd your answer because it uses the JS terminology and brought above analogy to mind ; ) – Alexander Varwijk Feb 24 '12 at 21:14
  • Thanks this one clarified it :) – Dev555 Feb 24 '12 at 21:17
  • 1
    Why didn't I find this answer when I searched for this last week!? This finally answered my question as well, many thanks! – Campbeln May 01 '14 at 05:12
  • Good to remember at this point is that when one declares a function with some param (like obj here) that obj is kind of new thing. It's like saying: `var obj; // undefined` inside that function. So if we are doing some assignments, we're actually altering local variable inside the function and we are breaking the "reference" (pointer). – Wojciech Fornal Oct 04 '15 at 22:46
30

Because JavaScript actually passes objects by pass-by-copy-reference.

When you pass my_obj into your test function, a copy of a reference to that object is passed in. As a result, when you re-assign the object in test, you're really only re-assigning a copy of a reference to the original object; your original my_obj remains un-changed.

Adam Rackis
  • 82,527
  • 56
  • 270
  • 393
  • 2
    If it is copy, then it why does it work in first example i have posted ? In my first example also then it should remain un-touched ? – Dev555 Feb 24 '12 at 21:08
  • 2
    @Dev555 - it's a copy of a **reference to** the object - I edited to be more clear. In your first case, obj is a copy of a reference that points to your real object. Adding a property will work fine. If I were there in person I could draw some pictures of boxes with arrows that I think would help a lot :) – Adam Rackis Feb 24 '12 at 21:10
  • So how to initialize objects? Initially i had this `instance.initGrid($(instance.mainEntityContainer), instance.mainEntityList); instance.initGrid($(instance.dependantEntityContainer), instance.dependantEntityList);`, I had to transform this to the following: `instance.mainEntityList = instance.initGrid($(instance.mainEntityContainer)); instance.dependantEntityList = instance.initGrid($(instance.dependantEntityContainer));` Still I wonder how to "free memory" from previous initializations. Whether I just have to make instance.dependantEntityList = null somewhere? I have "= new ..." inside. – Alexander Dec 01 '15 at 16:55
27

Because you are overwriting the reference, not the object.

// Create a new object and assign a reference to it
// to the variable my_obj
var my_obj = { name: 'foo' };

// Pass the reference to the test function
test(my_obj);

// Assign the reference to a variable called obj
// (since that is the first argument)
function test(obj) {
// Create a new (empty) object and assign a reference to it to obj
// This replaces the existing REFERENCE
    obj = {};
}
// my_obj still has a reference to the original object, 
// because my_obj wasn't overwritten
alert(my_obj.name); // foo
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • You are overwriting the copy of the reference, not the reference. If you would overwrite the reference itself, everything would be fine. – Alex Apr 23 '14 at 14:32
  • 3
    So how would you overwrite `my_obj` if you wanted to? – CodyBugstein Dec 24 '14 at 11:18
  • @CodyBugstein Wrap it in another object. Another answer on this question, http://stackoverflow.com/a/13452001/841830, shows this, along with some other possible approaches. Or, if you don't like any of those approaches, I think the answer is basically: you can't overwrite `my_obj`. – Darren Cook Mar 07 '17 at 16:12
5

Javascript lacks support for passing by reference (although objects are passed by reference and the reference is maintained as long as it is not overwrited with assignment eg. using =), but you can imitate ref keyword of C# using the following technique:

function test(obj) {
  obj.Value = {};
  //obj.Value = {name:"changed"};
}

var my_obj = { name: 'foo' };

(function ()
{
  my_obj = {Value: my_obj};
  var $return = test(my_obj);
  my_obj = my_obj.Value;
  return $return;
}).call(this);

alert(my_obj.name); // undefined, as expected
                    // In the question this returns "foo" because
                    // assignment causes dereference

Of course you can use globals and call function without arguments, in which case the references are not missed like this:

var obj = { name: 'foo' };
function test() {
    obj = {};
}
test();
alert(obj.name); // undefined

If you have all your code in closure, then things are simpler and above like globals doesn't pollute global namespace:

(function(){
    var obj = { name: 'foo' };
    function test() {
        obj = {};
    }
    test();
    alert(obj.name); // undefined
}).call(this);

The above "globals inside closure" -technique is nice if you have to port to Javascript some C# code which has ref arguments. Eg. The following C# code:

void MainLoop()
{
   // ...
   MyStruct pt1 = CreateMyStruct(1);
   MyStruct pt2 = CreateMyStruct(2);
   SwapPoints(ref pt1, ref pt2);
   // ...
}
void SwapPoints(ref MyStruct pt1, ref MyStruct pt2)
{
    MyStruct tmp = pt1;
    pt1 = pt2;
    pt2 = tmp;
}

could be ported to Javascript using something like:

(function(){
    var pt1, pt2;
    function CreateMyStruct(myvar)
    {
      return {"myvar":myvar}  
    }
    function MainLoop()
    {
       // ...
       pt1 = CreateMyStruct(1);
       pt2 = CreateMyStruct(2);
       console.log("ORIG:",pt1,pt2); 
       SwapPoints(); 
       console.log("SWAPPED:",pt1,pt2);
       // ...
    }
    function SwapPoints()
    {
        var tmp = pt1;
        pt1 = pt2;
        pt2 = tmp;
    }
    MainLoop();

}).call(this);

or if it's essential to use local variables and function arguments, then solution can be based on the first example of my answer like this:

(function(){
    function CreateMyStruct(myvar)
    {
      return {"myvar":myvar}  
    }
    function MainLoop()
    {
      // ...
      var pt1 = CreateMyStruct(1);
      var pt2 = CreateMyStruct(2);
      console.log("ORIG:",pt1,pt2); 

      (function ()
      {
        pt1 = {Value: pt1};
        pt2 = {Value: pt2};
        var $return = SwapPoints(pt1, pt2);
        pt1 = pt1.Value;
        pt2 = pt2.Value;
        return $return;
      }).call(this);

      console.log("SWAPPED:",pt1,pt2);
      // ...
    }
    function SwapPoints(pt1, pt2)
    {
      var tmp = pt1.Value;
      pt1.Value = pt2.Value;
      pt2.Value = tmp;
    }
    MainLoop();
}).call(this);

Really have to say that Javascript lacks much when it has not native ref! The code would be much simpler.

Timo Kähkönen
  • 11,962
  • 9
  • 71
  • 112