Why does JavaScript treat the scope of an array differently than it does the scope of other variables? Typically, if one passes a variable in a global scope into a function as a parameter, it is then local and changing it within the function does not change the value of the global variable. For example:
var globalInt = 1;
function test1(x){
x = x + 1
}
test1(globalInt);
console.log(globalInt); //globalInt is still 1
However, it appears the same does not apply when passing an array of values.
var globalArray = ["TEST"];
function test(x){
x[0] = x[0].toLowerCase()
}
test(globalArray);
//globalArray is now ["test"] instead of ["TEST"]
console.log(globalArray[0]);
This happens for me when I test in Chrome, but I have not tested it in other browsers so far. Why does this happen and does it happen in other browsers?
This is simply because an array (which is an Object) are passed by reference while primitives are not. Note that technically it is passed by value but in this case that value is a reference, thanks Esteban
A object is automatically passed by reference, without the need to specifically state it
If you pass an object (i.e. a non-primitive value, such as Array or a user-defined object) as a parameter and the function changes the object's properties, that change is visible outside the function, as shown in the following example:
MDN Link
Yes... JavaScript objects and arrays (which are objects) are passed by reference. But you should remember to read the method signature explicitly because some array methods don't alter the original array but return a new one. Read the MDN documentation carefully about the method in question. Array.prototype.map() would actually leave the original array intact and return a brand new array which you'd need to set to assign to a variable.
Related
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.
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);
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.
It's not correct. What that describes is precisely called pass-by-value. JavaScript, like Java, only has pass-by-value. There is no pass-by-reference.
You understanding is correct. Every value in JavaScript is either a primitive, or a reference (pointer to an object). An object itself can never be a value directly. When you pass or assign a reference (pointer to an object), the new copy of the pointer sees the same object as the original pointer. But they are still two different pointer variables.
I know you've answered your own question with your 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.
but because that edit made me suddenly grok how this worked... which I will admit I was struggling with here is a code sample and pen that I think really demonstrates it.
x = {};
y = x; //point y reference to same object x is pointed to
console.log(x === y) //true both pointed at same object
function funky(o) //pass by value a reference to object
{
o = null; //change reference to point to null away from object
}
funky(x);
console.log(x)//not null still an object
var myObj = {
value: "Hello"
};
function change(localObj) {//pass reference to object
localObj.value = "Bye";//change property through the reference
localObj = null;//point the reference to null away from object
}
console.log(x === y) //still same reference;
change(myObj);
console.log(myObj.value) // Prompts "Bye"
console.log(myObj) //not null - its an object;
x = myObj; //point x to different object
console.log(x === y) //false pointed at different object;
console.log(x);
console.log(y);
https://codepen.io/waynetheisinger/pres/YewMeN
"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 (i.e. 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 e.g. 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.
Primitive values like numbers, strings, etc aren't passed by reference, only objects. For example:
var myObj = {
value: "Hello"
};
function change(localObj) {
localObj.value = "Bye";
}
change(myObj);
console.log(myObj.value) // Prompts "Bye"
When a variable refers to an object the value is a reference to the object (Referred from: Javascript by reference vs. by value )
function fun1(obj) {
obj.first = 1; //it affects the object ->obj
}
var myObj1 = {first: 0};
fun1(myObj1); //sending the object to the function
console.log(myObj1.first); // 1
But I would like to change the variable from an object, for example
function fun2(obj) {
obj = 1; }
var myObj2 = {first: 0};
fun2(myObj2.first);
console.log(myObj2.first);
Is there any way to achieve this?
Is there any way to achieve this?
Not directly. All you can do is pass an object and have the function modify a property on it, as in your first example. JavaScript does not have pass-by-reference, just pass-by-value. (The value may be an object reference, of course, but it's still a value.) There's no way in JavaScript to directly modify the variable/property you're passing into the function, because what the function receives is a copy of the value of that variable/property, not a reference to that variable/property.
Just to be clear about something: In your first code block, you said you were "sending the object to the function." That's incorrect. You send a reference to the object to the function.
This is the key thing to understand: Variables, properties, and function arguments (collectively, "variables") contain values, and those values are copied when you use assignment or when you pass them into functions. The value that refers to an object is called an object reference, because the value isn't the object, it's a reference to (pointer to) the object elsewhere in memory. The reference is copied when you pass it into a function, the object is not.
Don't confuse the "reference" in "object reference" with the "reference" in "pass-by-reference," they're completely different things. (In pass-by-reference, the reference is to the variable, not an object. JavaScript doesn't have pass-by-reference.)
I'm reading that in JavaScript, a common point of confusion arises because variables of primitives are passed by value, and variables of objects are passed by reference, while in function arguments, both primitives and references are passed by value.
In the course of my tinkering, I've made up the following code, but am having trouble wrapping my head around it.
> function setName2(obj) {
... obj.name="matt";
... obj = new Object();
... obj.name="obama";
... }
If I set
var person = new Object();
person.name = "michelle";
Then run
> setName2(person);
I get
> person.name;
'matt'
Which makes sense because the new object created is a pointer to a local object, hence not affecting the property of the global 'person'.
However, what if I first set
var obj = new Object();
obj.name = "michelle";
Then run
> setName2(obj);
?
I get the same outcome. Does this mean that the compiler recognizes the two variables of the same name (obj global and obj local) as references to different locations within the heap, each having some different pointer association, or is there a different explanation for this phenomenon?
JavaScript does not have pass-by-reference; everything is passed by value. However, some values are themselves references to objects. This distinction (pass-by-reference vs. is-a-reference) leads to much confusion.
An example to clear things up:
function f(o) { ... }
var obj = { foo: 42 };
f(obj);
No matter what you do in f, obj will always refer to the same object because the argument is not passed by reference. However the value obj, which is copied into o, is itself a reference to an object. This is why any property changes made to o inside the function will be visible on obj after it returns.
while in function arguments, both primitives and references are passed by value.
This is not true. There is nothing special about function arguments.
function setName2(obj) {
This accepts a reference to an object as an argument.
obj.name="matt";
This modifies the name property of the object that reference points to.
obj = new Object();
This replaces the reference to the original object with a reference to a new object.
obj.name="obama";
This modifies the name property of the new object. The original object is unchanged.
The confusion comes from the fact that "passed by reference" is misinterpreted by people or used in a wrong sense.
Parameters are passed by value. This means that changing the value inside the method doesn't change the original value.
In case of primitives, the value of a primitive is its value.
In case of objects, the value of an object is a reference to it. You can access and change object's content but you can't change the value of the reference itself.
In other programming languages, like C++ or C#, "passing by reference" means that you pass:
- a reference to a primitive type
- a reference to a reference to an object
In such case, not only the content of an object can be changed but also a reference itself can be changed.
There is NO passing by reference in Javascript.
Javascript uses pass-by-value.
The confusion is that objects are hold by reference variables (kind of pointers). In fact most common languages (java, javascript, etc.) do not have a real pass-by-reference behaviour. Another way to understand this could be pass-reference-by-value, although, formally, there is not such a thing.
That means when you pass an object as a parameter, you are actually passing a reference to the object by-value.
function setName2(obj) {
...
}
setName2(person);
here the contents of person (a reference, or "pointer" if you like) is copied by-value to a new local variable: obj.
obj and person are different variables that hold a reference to the same object.
So, doing obj = new Object(); makes obj to point to the new object. But person is unaffected since it is still a completely different variable.
I don't know where you read that, but it's absolutely not true. Objects are passed by reference, full stop. Whether or not it's a function parameter is completely irrelevant.
If in javascript object properties are passed by reference why this doesnt work:
var myObj={a:1}
function myFun(x){
x=2;
}
myFun(myObj.a);
// value myObj.a is still 1
but on the other hand if you do this:
var myObj={a:1}
function myFun(x){
x.a=2;
}
myFun(myObj);
// this works myObj.a is 2
Primitive values are passed by value. Objects are passed by reference.
Object properties are passed by based on their data type.
Here you are passing an integer - x represents the value 1. Assigning x the value 2 does not reference the original object.
Let's say the property you pass in is an array. And the 2nd function I call receives an array and you make changes to that array. Then the changes will persist to the object because the object's property contains a reference to the array you modified. You didn't technically modify the object at all... you just modified the array which is referenced in the object. When you pass an object property to a function, it's not aware that it belongs to an object at all.
See example, similar to yours:
var myObj={a:[1]}
function fn1(x){
x=2; //Overwrites x in this scope to the new primitive 2.
//This isn't reflected in myObj because x is not a
//reference to myObj.a it is a reference to the array
//that myObj.a contains (the [1]).
}
function fn2(x){
x.push(2);
}
fn1(myObj.a); //myObj.a is [1]
fn2(myObj.a); //myObj.a is [1,2]
your first example doesn't work, because object properties are not passed by reference (unless the property itself also is an object).
objects, as you noticed, are passed by reference - thats the reason your second example works.
When you pass a base data type, it is passed by value. That is for integers, you pass the parameter by value so a copy of it is made in the local scope. Objects however are passed by reference so the function has access to the variable. You could pass it by reference, but its easier to do
Obj.a=fun(Obj.a);
Consider the following code
function add(x, y) {
alert(arguments.length);
var total = x + y;
return total;
}
add(); // NaN alerts 0
add(2,3); // 5 alerts 2
add(3,3,5); //6 alerts 3
Where is arguments defined? How come it is available inside my add function?
It is automatically created for all functions.
See https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments
A detailed explanation about when and how the arguments object is created:
From the ECMAScript specification:
10.1.8 Arguments Object
When control enters an execution context for function code, an arguments object is created and
initialised as follows:
The value of the internal [[Prototype]] property of the arguments object is the original Object prototype object, the one that is the initial value of Object.prototype (see 15.2.3.1).
A property is created with name callee and property attributes { DontEnum }. The initial value of this property is the Function object being executed. This allows anonymous functions to be recursive.
A property is created with name length and property attributes { DontEnum }. The initial value of this property is the number of actual parameter values supplied by the caller.
For each non-negative integer, arg, less than the value of the length property, a property is created with name ToString(arg) and property attributes { DontEnum }. The initial value of this property is the value of the corresponding actual parameter supplied by the caller. The first actual parameter value corresponds to arg = 0, the second to arg = 1, and so on. In the case when arg is less than the number of formal parameters for the Function object, this property shares its value with the corresponding property of the activation object. This means that changing this property changes the corresponding property of the activation object and vice versa.
That is just a standard feature of Javascript. Whenever any function is called, there is an arguments array automatically added to the local scope. This allows a function to receive a variable or unknown amount of parameters from the caller, and dynamically use these as necessary.
Commonly this is used when one function is placed as a wrapper around another where the exact parameters are unknown and arbitrary to the wrapper, who simply performs an action and then passes the provided arguments directly into the wrapped function.
arguments is a property of function objects. See Using the arguments object or Property: Function: arguments for more information.
It's worth noting that arguments is not a "real" array, the documentation calls it an "array-like object" - more in Turning JavaScript's arguments object into an array.