Assigning to a new value to an object passed into a function in JavaScript
I'm new to JavaScript (though experienced in C++), and today, I wrote something like this:
function foo(bar) {
bar = "something else";
}
var x = "blah";
foo(x);
alert(x); // Alerts with "blah", but I was expecting it to alert with "something else"
This confused me a lot, as I've been watching some JavaScript videos by Douglas Crockford, and remember him saying something like "JavaScript is always pass by reference".
The way I can explain this situation is that JavaScript passes references to objects, but those references are copied. This would mean in the foo
function, I am assigning a new reference to bar
, which then goes out of scope, leaving to reference that x
has left untouched. Essentially we start with:
x ---->"blah"
Then when foo
is called, bar
references the same data:
x ---->"blah"
bar -----^
So when "something else" is assigned to bar
, this happens:
x ---->"blah"
bar ---->"something else"
Is that an accurate model of what is actually happening in JavaScript, or am I missing something else?
As an extra question, is there any way to say, change the data referenced by this variable? Is this a situation that comes up often, or can it be easily avoided?
Edit:
Douglas Crockford in the video I watched says "objects are always passed by reference they're not passed by value", which is correct, but arguments to functions are passed by value, it's just the reference is passed by value.
"JavaScript is always pass by reference" is, well, a [white] lie and a confusion of terms. While there is some "gray area", I go by these definitions of evaluation strategies.
Here are my arguments and reasoning. If you hold a different view, make sure that you can at least support it.
Call By Reference
Call By Reference means (to many people) that assigning to a parameter affects bindings in the caller.
In call-by-reference evaluation (also referred to as pass-by-reference), a function receives an implicit reference to a variable used as argument, rather than a copy of its value. This typically means that the function can modify (ie assign to) the variable used as argument—something that will be seen by its caller.
This is not the case in JavaScript, as the original post has noted in the observed behavior. Re-assigning a parameter (which can be thought of as a local variable with a dynamically supplied value) has no affect on any supplied arguments.
Sometimes, "Call By Reference" is [confusingly] used to mean "Call By Sharing" or "Call By Value [of the Reference]" as discussed next; true Call By Reference is found in languages like C++ and VB, but not JavaScript.
Call By [Object] Sharing
JavaScript's calling conventions can be discussed entirely in terms of Call By [Object] Sharing semantics.
All JavaScript objects are values; and all primitive values (which are a subset of all values) are immutable.
The semantics of call by sharing differ from call by reference in that assignments to function arguments within the function aren't visible to the caller, so eg if a variable was passed, it is not possible to simulate an assignment on that variable in the caller's scope. However since the function has access to the same object as the caller (no copy is made), mutations to those objects, if the objects are mutable, within the function are visible to the caller, which may appear to differ from call by value semantics.
An example of these Shared mutations is provided in ultrayoshi's answer and can be explained simply: when an expression (such as a variable access) evaluates to an object, and said object is passed to a function, no copy/clone is made.
Call By Value [of the Reference]
While the terminology "Call By Value [of the Reference]" is often used to describe the behavior, it should be noted that JavaScript does not have "references" (or "non-reference" values) in the sense of Java/C# so this terminology is subtly misleading - at least it's not saying Call By Reference, with it's various connotations, and many people understand a low-explanation.
In call-by-value, the argument expression is evaluated, and the resulting value is bound to the corresponding variable in the function .. If the function or procedure is able to assign values to its parameters, only its local copy is assigned — that is, [any variable] passed into a function call is unchanged in the caller's scope when the function returns.
Because only a "reference" to an object is passed (and not a copy/clone of said object), the semantics are merely that of Call By Sharing. However, I avoid this terminology in JavaScript because then it brings in unnecessary implementation details and also introduces a divide in how implementations pass objects vs primitive values.
The description "call-by-value where the value is a reference" is common (but should not be understood as being call-by-reference); another term is call-by-sharing.
Thus, when I'm talking about calling conventions in JavaScript,
I prefer to use Call By Sharing to discuss the behavior and I avoid Call By [Value/Reference] as they have too many different "meanings" and drag in unnecessary implementation details.
Your interpretation is spot on.
First, you have a variable called x
which is a reference to a string object. Let's say that memory is 0x100. x
points to 0x100
, which contains the bytes blah:
var x = "blah"; // x is 0x100 which references a string in memory
Next, you pass 0x100
into the function foo
:
function foo(bar) {
bar = "something else";
}
As everything in JavaScript is passed by value, even references , JavaScript makes a copy of this reference in memory, which is now called bar
within that function:
foo(x); // Copies the value of x (a reference) to bar
At this point, we have two separate variables. x
and bar
. Both happen to have the same value, 0x100
. Thus, if you were to change a property of the object either of those is referencing, it would affect both x
and bar
.
However, what you're doing is assigning bar
to point to something else:
bar = "something else"; // Now references some other string we just created
Now, bar
gets re-assigned to reference a new string we've just allocated memory for. bar
no longer has a value of 0x100
, it now has a value of some other address (say 0x500
). x
of course still has a value of 0x100
since bar
was merely a copy of x
, and not a reference to x
.
For this reason, when you:
alert(x);
You'll still get the original value, as that is what x
is pointing to.
Second question:
is there any way to say, change the data referenced by this variable? Is this a situation that comes up often, or can it be easily avoided?
Yes, just wrap it in another object. For example:
var x = {Value: "blah"};
foo(x);
Now, we have a reference to an object with a property called Value
, which contains a reference to a string in memory somewhere.
In foo
, we can do:
bar.Value = "something else";
Which will affect the Value
property of x
, since both bar
and x
referenced the same object, and you never changed the value of either of them.
In other words, you cannot re-assign the reference you're passing into a function, since you're simply re-assigning a copy. You can, however, change a property of an object being referenced, since other copies of that reference all point to the data you're changing.
Your interpretation is correct.
You can change the values of keys in an object, which lets you do something similar to pass-by-reference:
function foo(bar) {
bar.msg = "something else";
}
var x = { msg: "blah" };
foo(x);
alert(x.msg);
链接地址: http://www.djcxy.com/p/77580.html