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
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.
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)
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.
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. Number
s) 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.
}
}
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.
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 {}