Javascript: Pass by reference or value?
|
If you search the web for the topic of how JavaScript passes parameters into functions, you'll get a mixture of thoughts on it, many of which are, unfortunately, wrong. These thoughts are more than opinions; they're based on the experiences of the people making the statements. But unfortunately, too often a decision is made only on partial information. I'll get to the point: Regardless of what you might read, JavaScript always passes by value and does not pass by reference. JavaScript does not use a mixture, passing by reference for objects and arrays, and value otherwise.
First, here's a demonstration. Create a function such as this:
function reftest(a) {
a = 10;
}
That's simple enough; the function takes a parameter, a, and sets its value to 10. Now for the test: What will happen to the object passed in when you call this function?
x = [1,2,3];
y = 5;
z = "abc"
reftest(x);
alert(x);
reftest(y);
alert(y);
reftest(z);
alert(z);
When I ran this code, I saw three alert boxes open in sequence, each one displaying the values of the original, unchanged values: [1,2,3], 5, and "abc". In other words, the function did not change these variables. If JavaScript had used pass-by-reference, the values of these variables would all have changed to the integer value 10. Thus, we may conclude that JavaScript passes by value. But why do some people claim JavaScript uses a mixed paradigm? Because this pass-by-value feature has interesting effects when you pass in an object. Look at this example:
function reftest2(a) {
a.x = 10;
}
c = {x:1, y:2}; If you go strictly by what I just said, you might assume that the reftest2 function gets a copy of the object passed into it, and modifies the x member of that copy, leaving the original unchanged. But that's not what happens! The x member of c does indeed get changed by the function from 2 to 10. From this, it would be easy to surmise that JavaScript uses pass-by-reference when dealing with objects. But it still doesn't. Try calling the original reftest function, passing the an object, and you'll see the object doesn't change. The confusion here results in what exactly the value of a variable is. When an object is passed into a function, its internal address is what gets passed in. That's the "value" of the object. Thus when the function modifies the x member of the object it received, it ends up modifying the same x member as that in the original object. While this might seem like pass-by-reference, it's still pass-by-value. (If you're a C programmer, this should be totally familiar to you. C uses only pass-by-value as well, and you run into similar behavior when you pass an array into a function.) So why does it matter? Because you need to know exactly what the functions you create will do in all cases; otherwise you might end up with some nasty bugs! |


Comments (6)
to: Jeff Cogswell
re: javascript pass-by-whatever
Thu 8/30/2007 2:13 pm
Well just to make trouble -- you're wrong!
// The confusion here results in what exactly the value of a variable is. When an object is passed into a function, its internal address is what gets passed in. //
So you *say*; you have *special knowledge*. I would put it, "javascript automatically passes objects by reference". In c,
----------------------------------------
#include
typedef struct {
int bingo, bongo;
} ASTRUCT;
void afunc(ASTRUCT a) {
a.bingo = 8;
}
void bfunc(ASTRUCT *a) {
a->bingo = 8;
}
void main (void) {
ASTRUCT a;
a.bingo = 7;
afunc(a);
printf("%d\n",a.bingo);
bfunc(&a);
printf("%d\n",a.bingo);
}
----------------------------------------
It prints
7
8
I had to *tell* C to do pass-by-reference with the & and * operators.
If javascript *on its own* decides to treat a class of variables -- objects -- differently than others, and treats them as pass by references -- then that's what it's doing. Mumbo-jumbo about "its internal address" doesn't clarify; the language is defined by its syntax, and what it does in the privacy of its own compilation/interpretation is its problem; what is does *from our point of view* is treat objects differently than plain-old-variables.
-- best wishes
j.g. owen * * * * * * * * * * * * * * * * * * *
web: http://owenlabs.home.att.net/
email: owen_bda4@yahoo.com
* * * * * * * * * * * * * * * * * * * * * * * *
Posted by j.g. owen | August 30, 2007 2:36 PM
Thanks for the comment!
Good points, and good examples. I certainly can't argue with the idea that we can perceive JavaScript as passing objects by reference. I do, however, hesitate to actually say it does.
The reason is that if I'm creating a library and somebody else is using the library, I want to make absolutely sure that the other person and I are both clear on how my library handles the objects passed into its functions. As long as we're both clear, then choice of words "pass by value" vs "pass by reference" may not matter, I suppose. But if we're not clear because I say one thing and the user interprets it another way, then that's just asking for bugs.
However, what is perhaps more important than simple terminology is that people completely understand the mechanism. Can a function in JavaScript change the variable itself (rather than just its members)? Here's some code that attempts to do it, but fails:
a = {x:10, y:20};
function myfunction(q) {
q = {member1: 'a', member2: 'b'};
}
myfunction(a);
alert(a.member1);
In this case, is the object passed in by reference or by value? While it may not matter whether in my mind I consider it by value or by reference, the fact remains that the object "a" does *not* change into {member1: 'a', member2: 'b'}. Understanding this is vital to writing correct, bug-free code in JavaScript, whether we call it passing by value or by reference.
Posted by Jeff Cogswell | August 30, 2007 4:25 PM
to: Jeff Cogswell
re: javascript pass-by-whatever
Wed 9/05/2007 3:37 pm
Well just to footnote this fascinating discussion, I was perusing "Java in a Nutshell" (O'Reilly 1999) -- and under the heading "Terminology: Pass by Value" (p 74) author Flanagan agrees with Cogswell:
"Java ... is a 'pass by value' language. However, when a reference type is involved, the value that is passed is a reference. But this is not the same as pass-by-reference. If Java were a pass-by-reference language, when a reference type was passed by a method, it would be passed as a reference to the reference."
Which, for me, is gratifying in a way; I like a traditional obfuscation. I will still go to the grave believing a reference that is passed is indeed passed by reference, but obviously will do so as an ignorant non-believer....
... I just realized this note may help to fuel any Javascript/Java confusions lingering about -- but I can't help but feel that's somehow appropriate....
-- best wishes,
j.g. owen * * * * * * * * * * * * * * * * * * *
web: http://owenlabs.home.att.net/
email: owen_bda4@yahoo.com
* * * * * * * * * * * * * * * * * * * * * * * *
Posted by j.g. owen | September 5, 2007 3:39 PM
It seems to me that there is flaw in your example. When you have the function variable 'a' and you assign to it, whatever it was referring to before doesn't change, the variable 'a' just gets a new value. This means that you can have multiple variables referring to 'a', and when you reassign 'a', that variable will now refer to a different object. Other variables that referenced the original object will still reference the original object. If however 'a' is an object and you set a property of that object, it will be reflected on the original object, because 'a' isn't "reassigned", the value of its property is. When languages are "pass by value" my understanding is that they make copies of the variables being sent to a function. Pass by reference just means that instead of making copies of all the values in an object, a reference to the object is passed. I wholeheartedly agree with what I perceive to be your *main* point. It doesn't matter what we call it, it is important to understand how making changes to a function parameter will affect the variable that was passed.
Posted by adam83 | January 10, 2008 11:42 PM
Hi,
First, I'd like to point out that:
1. JavaScript is not Java, nor is it a form of Java.
2. JavaScript is not C, but that's kind of obvious.
So, while principle definitions may apply, the specifications are different and implementation of the specification are open to interpretation.
JavaScript, or ECMAScript (The official name) has it's own specifications which can be found at http://www.ecma-international.org/publications/standards/Ecma-262.htm.
When passing a variable to a function, it in fact makes it's own local scope variable containing the value and/or properties/attributes depending on the parameter type passed in to a function.
*All parameters are passed by value*
Paramaeter Passing Rules:
a. Value type = pass by value (Creates new local variable)
b. Object type = pass by value (Creates new local variable) the address of the object, which in "effect" is reference to the original object, but the address is still passed by value.
c. You are unable in any way to pass a value type by reference.
Note: Reference types exist in JavaScript, but are internal use only by definition of the specification.
If you pass in an Object (ECMA-262: ECMAScript Language Specification - An Object is an unordered collection of properties. Each property consists of a name, a value and a set of attributes.) the properties of the object become the properties of the local scope variable. But the properties point to the original properties.
Creating a new Object in the local scope does just that, creates a new Object.
************snippet from Jeff Cogswell*******
a = {x:10, y:20};
function myfunction(q) {
q = {member1: 'a', member2: 'b'};
}
myfunction(a);
alert(a.member1);
**************************
Your code above creates a "NEW" Ojbect for local scope variable q.
However, you could do the following and it would add the properties member1, member2 to the original Object.
************************
a = {x:10, y:20};
function myfunction(q) {
q.member1= 'a';
q.member2= 'b';
}
myfunction(a);
alert(a.member1);
*********************
If you want to return multiple values from a function, wrap your value type variables in an object, pass in the object, do something, then assign the object properties to your local variables upon return from the function.
Here is a quick sample I threw together.
--------------Sample Code----------
// v is local scope to foo1
// if v is a data type the assignment will work on the local scope variable.
// if v is an object, assignment will create a new local scope v.
function foo1(v) {
write("In Foo1");
write("before v=" + v);
v = 30; // assigns to local scope v
write("after v=" + v);
write("v.x=" + v.x);
write("v.y=" + v.y);
write("a=" + a);
write("a.x=" + a.x);
write("a.y=" + a.y);
write("b=" + b);
write("b.x=" + b.x);
write("b.y=" + b.y);
write("--------");
}
// w is local scope to foo2
// if w is a Object the assignment will work on the local scope variable and the original properties.
// if w is a data type, assignment will fail silently.
function foo2(w) {
write("In Foo2");
write("before w=" + w);
write("before w.x=" + w.x);
write("before w.y=" + w.y);
w.x = 20; // create/assign property x - points to the original property if it exists
w.y = 55; // create/assign property y - points to the original property if it exists
write("after w=" + w);
write("after w.x=" + w.x);
write("after w.y=" + w.y);
write("a=" + a);
write("a.x=" + a.x);
write("a.y=" + a.y);
write("b=" + b);
write("b.x=" + b.x);
write("b.y=" + b.y);
write("--------");
}
function write(msg) {
document.write("
" + msg);
}
////////////////////////
// a and b are global scope
var a = 1;
var b= new Object;
b.x = 2;
write("(1)==== foo1(a)");
foo1(a);
write("After foo1(a)");
write("a=" + a);
write("a.x=" + a.x);
write("a.y=" + a.y);
write("b=" + b);
write("b.x=" + b.x);
write("b.y=" + b.y);
write("*****************************");
write("(2)=== foo1(b)");
foo1(b);
write("After foo1(b)");
write("a=" + a);
write("a.x=" + a.x);
write("a.y=" + a.y);
write("b=" + b);
write("b.x=" + b.x);
write("b.y=" + b.y);
write("*****************************");
write("(3)=== foo2(a)");
foo2(a);
write("After foo2(a)");
write("a=" + a);
write("a.x=" + a.x);
write("a.y=" + a.y);
write("b=" + b);
write("b.x=" + b.x);
write("b.y=" + b.y);
write("*****************************");
write("(4)=== foo2(b)");
foo2(b);
write("After foo2(b)");
write("a=" + a);
write("a.x=" + a.x);
write("a.y=" + a.y);
write("b=" + b);
write("b.x=" + b.x);
write("b.y=" + b.y);
write("*****************************");
var c = 5; // original c value
var d = 6; // original d value
var e = 7; // original d value
var k = new Object; // create a new object wrapper
k.x = c; // creates a NEW property
k.y = d; // creates a NEW property
k.z = e; // creates a NEW property
write("(5)=== foo2(c)");
write("c=" + c);
write("d=" + d);
write("e=" + e);
write("k.x=" + k.x);
write("k.y=" + k.y);
write("k.z=" + k.z);
foo2(k);
write("After foo2(k)");
write("c=" + c);
write("d=" + d);
write("e=" + e);
write("k.x=" + k.x);
write("k.y=" + k.y);
write("k.z=" + k.z);
c = k.x; // assign to local scope c
d = k.y; // assign to local scope d
e = k.z; // assign to local scope e
write("After assigning to local c,d,e");
write("c=" + c);
write("d=" + d);
write("e=" + e);
write("*****************************");
////////////////////////
--------------RESULTS--------------
(1)==== foo1(a)
In Foo1
before v=1
after v=30
v.x=undefined
v.y=undefined
a=1
a.x=undefined
a.y=undefined
b=[object Object]
b.x=2
b.y=undefined
--------
After foo1(a)
a=1
a.x=undefined
a.y=undefined
b=[object Object]
b.x=2
b.y=undefined
*****************************
(2)=== foo1(b)
In Foo1
before v=[object Object]
after v=30
v.x=undefined
v.y=undefined
a=1
a.x=undefined
a.y=undefined
b=[object Object]
b.x=2
b.y=undefined
--------
After foo1(b)
a=1
a.x=undefined
a.y=undefined
b=[object Object]
b.x=2
b.y=undefined
*****************************
(3)=== foo2(a)
In Foo2
before w=1
before w.x=undefined
before w.y=undefined
after w=1
after w.x=undefined
after w.y=undefined
a=1
a.x=undefined
a.y=undefined
b=[object Object]
b.x=2
b.y=undefined
--------
After foo2(a)
a=1
a.x=undefined
a.y=undefined
b=[object Object]
b.x=2
b.y=undefined
*****************************
(4)=== foo2(b)
In Foo2
before w=[object Object]
before w.x=2
before w.y=undefined
after w=[object Object]
after w.x=20
after w.y=55
a=1
a.x=undefined
a.y=undefined
b=[object Object]
b.x=20
b.y=55
--------
After foo2(b)
a=1
a.x=undefined
a.y=undefined
b=[object Object]
b.x=20
b.y=55
*****************************
(5)=== foo2(c)
c=5
d=6
e=7
k.x=5
k.y=6
k.z=7
In Foo2
before w=[object Object]
before w.x=5
before w.y=6
after w=[object Object]
after w.x=20
after w.y=55
a=1
a.x=undefined
a.y=undefined
b=[object Object]
b.x=20
b.y=55
--------
After foo2(k)
c=5
d=6
e=7
k.x=20
k.y=55
k.z=7
After assigning to local c,d,e
c=20
d=55
e=7
*****************************
Posted by h. shank | January 25, 2008 11:02 AM
j.g. owen:
Think of it this way. An object is a normal local int that holds a number which happens to be an address to a spot in memory large enough to hold your object.
Now, when you pass this to a function, you are NOT passing an object into the function, you are passing an int which holds the address of an object. Now, when you call a.x, javascript looks up the address contained in this int value, and manipulates the data at that address, NOT the value passed into the function. Therefore any object that happens to hold this address would point to the same spot, and will reflect whatever changes were made to member variables.
However, if you tried to modify the address of a. a = 0x43232343, then this would NOT modify the original address of a in the calling function. It would modify a local int value that lives on the stack for the called function.
So, if you were to make a new object, say a RegExp object: a = new RegExp("blaa");, what is happening is that memory is allocated on the heap to make room for this RegExp object, and the address is assigned to the local variable a, which is on the stack of the called function, and the original a in the calling function is not affected.
If it were pass by reference, the original a in the calling function would become a RegExp object, and point to the new location in memory, but since it isnt, the original a is still whatever object it was before the function was called, and it still points to its original address.
C is exactly the same way. A pointer is nothing more than an unsigned int that holds a memory location. This is why you have to use the * operator to modify its value. If you had a pointer (int* a) and you said a=3, your not modifying the value that a points to, your modifying the address that a holds.
Posted by computrius | January 29, 2008 9:58 AM