views:

68

answers:

2

My understanding: in Javascript objects and arrays get passed as references not values for function arguments. A jQuery group is an object and hence should be passed as reference.

However I'm finding in the test script below that something strange is going on; the jQuery group is behaving like a value not a reference unless wrapped in another object ... can anyone explain this?

<html>
<head>
 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"&gt;&lt;/script&gt;
</head>
<body>
<script>

 function test(arg){
   arg = arg.add($('<span/>'))
   console.log(arg);
 };

 ele = $('<div/>');
 test(ele);  // div + span in the group as expected
 console.log(ele); // only the div - the 'arg' param in function was a copy

 function test2(arg){
   arg.a = arg.a.add($('<span/>'));
   console.log(arg.a);
 };

 obj = {a:ele};
 test2(obj); // div + span in the group as expected
 console.log(obj.a); // both in the group - arg acted like a reference!

</script>
</body>
</html>
+2  A: 

This is a "feature" of the .add() method. It does not modify the original jQuery object, but rather returns a new object with the added value.

Given your first example, you would need to return the arg variable and overwrite ele.

 function test(arg){
   arg = arg.add($('<span/>'))
   console.log(arg);
   return arg;  // return the new jQuery object stored in "arg"
 };

 ele = $('<div/>');
 ele = test(ele);  // overwrite the original "ele" with the returned value
 console.log(ele); 

EDIT: To give another illustration, using your code, but with .push() which modifies the original object, you will see the correct value updated in ele.

 function test(arg){
   arg = arg.push($('<span/>')[0])
   console.log(arg);  // Because .push() is being used, "arg" will reference 
                      //   the new length of the array.
 };

 ele = $('<div/>');
 test(ele);  
 console.log(ele); // Will reference jQuery object with both elements

EDIT: Once last illustration. Because .add() returns a new object, you could update both variables to point to the same value like this:

ele = arg = arg.add($('<span/>'));

Now instead of ele referencing the original, and arg referencing the new Object that was created, both variables hold a reference to the same Object in memory.

patrick dw
*This is a "feature" of the .add() method.* - makes it sound like it would be possible with another method if it were coded as such. The fact is, a new value is set to the `arg` variable within the function and it doesn't modify the `ele` variable in the outer scope which the OP thinks has been passed in "by reference".
Andy E
@Andy E - Actually it is possible. I'll give another example using OP's original code, but with `push()` instead. It correctly modifies the jQuery object, and `ele` references that changed object.
patrick dw
@Andy E - Ok, maybe I'm misunderstanding the correct meaning of *"pass by reference"*. When you pass a variable, you are passing a reference to its value, which I believe the same sort of thing you would see in other object-oriented languages like Java for example. Am I mistaken? In any case, both solutions illustrate the proper concepts within javascript.
patrick dw
@patrick: Java is pass by value, very much like JavaScript is. *"Pass by reference"* is more accurately conveyed by languages such as C#, PHP or VB - where you can pass a variable into a function and any modifications by the function are reflected by the original variable that was passed in. Passing by reference is often used by functions that have an "in" and an "out" argument, where the function result would actually be set to the out argument rather than acting like JS's *return* statement.
Andy E
Although your *push()* example shows how it could be done, it only works because it modifies the properties of the object to which it has a reference to, which might confuse the OP a tad if he's used languages that pass by reference before. btw, not my downvote because it all depends on what the OP is expecting really. It seems like he's expecting something like [PHP's passing by reference](http://php.net/manual/en/language.references.pass.php).
Andy E
@Andy E - ...just saw your new comment so I deleted the one before this. Yes, we don't know exactly what the expectation was. I don't think OP was expecting a *"pass by reference"* situation where one variable is a reference to another. Note that OP didn't mention variable "passing by reference", but rather the Object getting passed *as* reference and not value. This would be the correct javascript concept where a reference to the value in memory is passed, as opposed to the value being effectively copied into the new variable. Hard to say, though.
patrick dw
Bodyscanner
A: 

The two tests you performed aren't the same. The first one sets a variable arg to the value arg.add(...), while the second one set's a property on arg named a to the value arg.a.add(...). Passing "by reference" in JavaScript isn't really the same as it is in other languages (in fact, some would disagree that it is really pass-by-reference). When you pass an variable whose type is an object, you have a reference to its value and not to the original variable.

When you set arg = arg.add(...), you are setting a new value for the arg variable and overriding its previous value. This will not modify the variable that was passed in, because you don't have a reference to it, you only have a reference to its value (the object).

Andy E