18

Object are passed with their reference in javascript. Meaning change in that object from any where should be reflected. In this case, the expected output was {} for console.log(a)

function change(a,b) {
    a.x = 'added';
    a = b;//assigning a as {} to b
}
a={}
b={}
change(a,b);
console.log(a); //expected {} but output {x:'added'}
console.log(b)

What is happening here? It should not be because of functional scope as far as I know. Thank you

7ochem
  • 2,183
  • 1
  • 34
  • 42
Suman Lama
  • 946
  • 1
  • 9
  • 26
  • 1
    Object reference is passed as functional parameters. So, whatever you change inside the function, it will affect the actual object. – Vigneswaran Marimuthu Jun 19 '15 at 05:32
  • @VigneswaranMarimuthu so the output sould be {x:'added'} right? but that is not the case here – Suman Lama Jun 19 '15 at 05:33
  • when you change properties of an object inside a function or anywhere else it works by reference, but if you assign another value to it, the global variable would not change as if the value becomes local. – iamawebgeek Jun 19 '15 at 05:35
  • 2
    Assigning `b` to `a` inside the function has no effect, since `a` in function scope is a different variable than `a` in global scope. – GOTO 0 Jun 19 '15 at 05:35
  • @Suman `a` and `b` are just parameters in scope of the function. Assignment doesn't do anything with actual variable `a` which is outside the function – Vigneswaran Marimuthu Jun 19 '15 at 05:36
  • 9
    I like how you say you know objects are passed by reference, then express great shock and surprise that objects are passed by reference. – Lightness Races in Orbit Jun 19 '15 at 05:40
  • @LightnessRacesinOrbit I was not shocked at the passing by reference I was shocked about reassigning the object – Suman Lama Jun 19 '15 at 05:42
  • Okay the expected output was an empty object as I have reassigned a = b; – Suman Lama Jun 19 '15 at 05:44
  • Right, I think I get it. You expected `=` on an object obtained by reference to affect the referent. Nah, this is pretty much the only way you can "rebind" the reference. It's basically the same thing that happened when you passed arguments into the function. – Lightness Races in Orbit Jun 19 '15 at 05:45
  • See this answer for an explanation/exploration of how javascript pass function arguments: http://stackoverflow.com/questions/13506398/why-are-objects-values-captured-inside-function-calls/13508654#13508654 – slebetman Jun 19 '15 at 08:49
  • JavaScript scope is acting weird? That is just _shocking_! – Mike G Jun 19 '15 at 13:04
  • Also see [*'Is JavaScript a pass-by-reference or pass-by-value language?'*](http://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language) – Radiodef Jun 19 '15 at 13:27
  • 1
    This is only confusing because the variables in the function parameters use the same name as the global variables. If the function parameter names were `foo` and `bar`, it might be easier to understand why it works the way it does. – Brandon Jun 19 '15 at 13:42

6 Answers6

37

If you added another line you can get a clearer picture of what is happening:

function change(a,b) {
    a.x = 'added';
    a = b;
    a.x = 'added as well';
};
a={};
b={};
change(a,b);
console.log(a);  //{x:'added'}
console.log(b);  //{x:'added as well'}

When you're doing a = b you're assigning the local variable a to the reference that b is holding.

Jesse Kernaghan
  • 4,544
  • 2
  • 18
  • 25
21

You are right that objects are passed by reference and any change made to the object in the function will be reflected everywhere. This is precisely why adding the x property in the function modified the object outside of it.

What you are missing is that the line a = b; does not modify the object, it modifies the reference to the object. You can pass both of the objects in another container object / array if you need to set the reference:

function change(container) {
    container.a.x = 'added';
    container.a = container.b;//assigning a as {} to b
}
var container = { a: {}, b: {}};
change(container);
console.log(container.a);
console.log(container.b)
Nikola Dimitroff
  • 6,127
  • 2
  • 25
  • 31
  • I think this one requires too many actions. Take a look at my [answer](http://stackoverflow.com/questions/30930560/scope-in-javascript-acting-weird/30930736#30930736) – iamawebgeek Jun 19 '15 at 05:58
  • 5
    But objects are *not* passed by reference! `a = b` does only modify the *local* reference (variable)! – Bergi Jun 19 '15 at 07:07
  • @Bergi and in this case? Are you serious? – iamawebgeek Jun 19 '15 at 07:39
  • @zazu: Probably I'm just nit-picking at the term [pass-by-reference](https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_reference) – Bergi Jun 19 '15 at 07:42
  • @Bergi I have read a book on PHP, that explains the engine of php objects work. It was written that by default objects are passed to functions by reference and you can change its properties and etc., but if you add an `&` sign before the variable, it would work with the base variable's pointer in the memory, that means you can both change the variable and just simulate the value. However in both cases they are passed by reference. – iamawebgeek Jun 19 '15 at 07:52
  • @zazu: Then that book seems to confuse call-by-reference with [call-by-sharing](https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing) - which are clearly different, and you shouldn't use the same term for both. – Bergi Jun 19 '15 at 08:07
  • 5
    "You are right that objects are passed by reference" – No, they aren't. JavaScript is strictly pass-by-value, and the OP's code demonstrates that fact quite nicely. If JavaScript were pass-by-reference, the OP's code would work and he would never have asked this question in the first place. – Jörg W Mittag Jun 19 '15 at 08:32
  • 1
    The term I am familiar with from the Java world, which I think would apply here as well, is pass-by-copy-of-reference. – Brandon Jun 19 '15 at 13:45
  • @JörgWMittag Bergi is right. But some people just mix up call-by-sharing with pass-by-reference, because there is no passing by reference in js. – iamawebgeek Jun 19 '15 at 13:51
  • 2
    @Bergi: That link is the answer :) It explains the OP's problem precisely. – Lightness Races in Orbit Jun 19 '15 at 15:27
13

The variable 'a' in the context of your function is not the same as the 'a' variable outside the function. This code is semantically equivalent to yours:

function change(foo,bar) {
    foo.x = 'added';
    foo = bar;//assigning foo as {} to bar
}
a={}
b={}
change(a,b);
console.log(a); //expected {} but output {x:'added'}
console.log(b)

It's obvious in this case that the 'foo' variable only exists inside the function, and doing foo = bar doesn't change a as the reference is passed by value.

  • 2
    This one gets it. `function change(a,b)` creates local variables *a* and *b*, so the assignment `a = b` assigns to the local *a* variable, not the global. – RobG Jun 19 '15 at 05:59
  • Put differently: `foo` does hold a reference to the object that is also referenced by `a`. `foo` is not a reference to `a`. – Bergi Jun 19 '15 at 07:17
9

Object are passed with their reference in javascript.

No, they aren't. ECMAScript/JavaScript is strictly pass-by-value. (More precisely, call-by-sharing, which is a special case of pass-by-value.)

What is happening here?

This is just normal pass-by-value.

Your confusion stems from the fact that you erroneously believe ECMAScript/JavaScript is pass-by-reference, when in fact it is not.

ECMAScript uses pass-by-value, or more precisely, a special case of pass-by-value where the value being passed is always a pointer. This special case is also sometimes known as call-by-sharing, call-by-object-sharing or call-by-object.

It's the same convention that is used by Java (for objects), C# (by default for reference types), Smalltalk, Python, Ruby and more or less every object-oriented language ever created.

Note: some types (e.g. Numbers) are actually passed directly by value and not with an intermediary pointer. However, since those are immutable, there is no observable behavioral difference between pass-by-value and call-by-object-sharing in this case, so you can greatly simplify your mental model by simply treating everything as call-by-object-sharing. Just interpret these special cases as internal compiler optimizations that you don't need to worry about.

Here's a simple example you can run to determine the argument passing convention of ECMAScript (or any other language, after you translate it):

function isEcmascriptPassByValue(foo) {
  foo.push('More precisely, it is call-by-object-sharing!');
  foo = 'No, ECMAScript is pass-by-reference.';
  return;
}

var bar = ['Yes, of course, ECMAScript *is* pass-by-value!'];

isEcmascriptPassByValue(bar);

console.log(bar);
// Yes, of course, ECMAScript *is* pass-by-value!,
// More precisely, it is call-by-object-sharing!

If you are familiar with C#, it is a very good way to understand the differences between pass-by-value and pass-by-reference for value types and reference types, because C# supports all 4 combinations: pass-by-value for value types ("traditional pass-by-value"), pass-by-value for reference types (call-by-sharing, call-by-object, call-by-object-sharing as in ECMAScript), pass-by-reference for reference types, and pass-by-reference for value types.

(Actually, even if you don't know C#, this isn't too hard to follow.)

// In C#, struct defines a value type, class defines a reference type
struct MutableCell
{
    public string value;
}

class Program
{
    // the ref keyword means pass-by-reference, otherwise it's pass-by-value
    // You must explicitly request pass-by-reference both at the definition and the call
    static void IsCSharpPassByValue(string[] foo, MutableCell bar, ref string baz, ref MutableCell qux)
    {
        foo[0] = "More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.";
        foo = new string[] { "C# is not pass-by-reference." };

        bar.value = "For value types, it is *not* call-by-sharing.";
        bar = new MutableCell { value = "And also not pass-by-reference." };

        baz = "It also supports pass-by-reference if explicitly requested.";

        qux = new MutableCell { value = "Pass-by-reference is supported for value types as well." };
    }

    static void Main(string[] args)
    {
        var quux = new string[] { "Yes, of course, C# *is* pass-by-value!" };

        var corge = new MutableCell { value = "For value types it is pure pass-by-value." };

        var grault = "This string will vanish because of pass-by-reference.";

        var garply = new MutableCell { value = "This string will vanish because of pass-by-reference." };

        // the first two are passed by value, the other two by reference
        IsCSharpPassByValue(quux, corge, ref grault, ref garply);

        Console.WriteLine(quux[0]);
        // More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.

        Console.WriteLine(corge.value);
        // For value types it is pure pass-by-value.

        Console.WriteLine(grault);
        // It also supports pass-by-reference if explicitly requested.

        Console.WriteLine(garply.value);
        // Pass-by-reference is supported for value types as well.
    }
}
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
7

Okay, so you've figured out that JavaScript objects have reference semantics, so modifying a referent has an effect on the same object in the original scope.

What you also need to realise is that = is not part of these rules; not only does it perform assignment, but it will also rebind the reference to a new object.

Under the hood, so to speak, that's basically how your original references were formed.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    Objects have reference semantics (i.e. you always pass around pointers, never the objects themselves), and argument passing is always pass-by-value and never pass-by-reference. (The latter one is the crucial bit the OP is missing, he seems to believe that JavaScript is pass-by-reference, which is not true.) – Jörg W Mittag Jun 19 '15 at 08:31
  • @JörgWMittag: It can be argued that JavaScript is pass-by-reference, and it can be argued that it is not. The terminology is not consistently used across the community and you cannot say categorically that one is "not true" but the other is. What you _can_ say is that JavaScript employs [pass by sharing](https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing) and that is the only precise, accurate and objective term that may be applied here. The rest is just personal preference on what term to pick when being imprecise. – Lightness Races in Orbit Jun 19 '15 at 15:29
  • Pass-by-sharing is a special case of pass-by-value. The OP thought that JavaScript was pass-by-reference, he wrote code under the assumption that JavaScript was pass-by-reference which would have worked if JavaScript was pass-by-reference but doesn't because it isn't pass-by-reference. I get that you don't want to trust random strangers on the internet, but do you trust JavaScript? Then you can ask it yourself: … – Jörg W Mittag Jun 19 '15 at 15:55
  • `function isEcmascriptPassByValue(foo) { foo = 'No, ECMAScript is pass-by-reference.'; return; }; var bar = 'Yes, of course, ECMAScript *is* pass-by-value!'; isEcmascriptPassByValue(bar); alert(bar); // Yes, of course, ECMAScript *is* pass-by-value!` – Jörg W Mittag Jun 19 '15 at 15:55
  • 2
    @JörgWMittag: It's almost like you didn't read anything I said. You're applying your own personal narrow definition of the terms and using it to make a concrete statement of fact, but you can't do that: the generally accepted meaning of both _does not amply cover JavaScript's semantics_. Read the article I linked you to. – Lightness Races in Orbit Jun 19 '15 at 15:56
-1

This should help to solve your problem:

var obj = {}, anotherObj = {};
// in case if they are not global, make them global or define parent scope to be able to modify inside the function
window.obj = obj;
window.anotherObj = anotherObj;
function swap(a, b) {
  window[a].newProp = 'XYZ';
  window[a] = window[b]; // now obj is gone, because replaced with anotherObj
}
swap('obj','anotherObj');
console.log(obj); // now it would give Object {}
iamawebgeek
  • 2,713
  • 1
  • 18
  • 34
  • I don't see how your answer relates to the question. And all that your code shows is that even objects and arrays are *not* not passed by reference (if they were, you wouldn't need that "scope"). – Bergi Jun 19 '15 at 07:10
  • @Bergi the idea is the same as Nikola's answer. The only differences are the container is `window` object and variable names as strings are passed to the function. If OP wanted the solution to his problem, but not just an explanation of what is happening, this one is exacly what he needs. – iamawebgeek Jun 19 '15 at 07:35
  • 3
    "Objects and arrays are passed by reference in default." – No, they aren't. JavaScript is strictly pass-by-value, and the OP's code demonstrates that fact quite nicely. If JavaScript were pass-by-reference, he OP's code would work and he would never have asked this question in the first place. – Jörg W Mittag Jun 19 '15 at 08:29
  • @JörgWMittag no sense for argueing about how to understand this phrase. I have already debated with Bergi higher and expressed my opinion. The key is that the solution is fit for what OP wants. – iamawebgeek Jun 19 '15 at 13:56