This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Copying array by value in javascript
i have a funny problem with javascript. i copy an array variable to make modifications on the copy only, then splice the copy to delete an element. however the original array variable is affected by the splice - as if the copy was a 'copy by reference':
window.onload = function() {
var initial_variable = ['first', 'second', 'third'];
var copy_initial_variable = initial_variable;
copy_initial_variable.splice(0, 1);
alert('initial variable - ' + initial_variable);
};
//output: initial variable - second,third
firstly, is this intentional behaviour for javascript or is it a bug?
and secondly, how can i make a copy of an array and delete an element in the copy but not in the original?
one thing which makes me think that the above may be a javascript bug is that this behaviour only happens with arrays and not with integers. for example:
window.onload = function() {
var initial_variable = 1;
var copy_initial_variable = initial_variable;
copy_initial_variable = 2;
alert('initial variable - ' + initial_variable);
};
//output: initial variable - 1
if the behaviour were consistent then this ought to output 2 since the assignment would presumably be by reference?
This is in no way a bug, but a very common misunderstanding. Let's see what happens when I say
var a = b;
Integers and other javascript primitives, like floats and booleans, are "assigned by value".
Which means that whatever value b has is going to be copied to a. To the computer, it means having the part of memory that b references copied to the memory that a references. That's the behavior you were expecting.
When arrays and other objects (and "descendants" of a new Object() call) are used like that, there is a copy by reference. Meaning that the value of a now references the value of b, the memory that b references isn't copied or modified. Thus, when writing
a = [1,2,3];
b = a;
b and a become interchangeable. They're referencing the same memory address. To achieve what you're trying to do, use
var copy_initial_variable = initial_variable.slice(0);
Read Does JavaScript pass by reference? for more information.
In first case you are working with arrays, which are passed by reference. And in second case you are working with prime types which are passed by value. In first case you should copy initial array (e.g. with initial_variable.slice(0)). Try something like
window.onload = function() {
var initial_variable = ['first', 'second', 'third'];
var copy_initial_variable = initial_variable.slice(0); //returns new array!!!!
copy_initial_variable.splice(0, 1);
alert('initial variable - ' + initial_variable);
};
The problem isn't in how splice behaves, but the fact that initial_variable and copy_initial_variable reference the same array.
alert (copy_initial_variable === initial_variable);
In JavaScript there are two types of values: primitive values, like numbers and booleans, and objects, including arrays. Variables hold primitive values, but they hold references to objects. "Copying" primitive values works as you expect, a new primitive value is created so that changing the copy variable will not change the original variable. But "copying" an object actually copies the reference pointing to that object, it doesn't create a new object.
It is not a JavaScript bug, this is the intended behavior.
Related
This question already has answers here:
Pass variables by reference in JavaScript
(16 answers)
Closed 1 year ago.
My problem can be reduced to this trivial (or so it seemed) bit of code
var myArray = [1,2];
function addOnetoArrayElement(element){
element +=1;
console.log(element); // Returns 3
}
addOnetoArrayElement(myArray[1]);
console.log(myArray[1]); // Returns 2
I find in fascinating that one can do all sorts of operations on arrays like map, reduce, push to an array but no reference at all to doing simple arithmetic operations of elements of an array. Never mind all that stuff about hoisting,scope, passing by value and reference. I'm sure that maybe someday I'll know enough to contribute to that discourse. What must I do to make myArray[1] = 3?
You assume that myArray[1] will be passed to the function in a way that it can modify that element in myArray (so-called by reference).
But myArray[1] is just a number, it's a primitive type and is passed by value. It means a copy is passed to the function which has no connection to myArray. As a result modification of element param does not change myArray (see docs).
Primitive parameters (such as a number) are passed to functions by value; the value is passed to the function, but if the function changes the value of the parameter, this change is not reflected globally or in the calling function.
For reference there are just 7 primitive data types in JS: string, number, bigint, boolean, undefined, symbol, and null.
To fix the issue we need to pass whole myArray to function as first parameter (by reference because it's an Array) and index as second. In this case myArray will be passed by reference and the function will be able to modify it.
var myArray = [1,2];
function addOnetoArrayElement(arr, ind){
arr[ind] += 1;
console.log(arr[ind]); // Returns 3
}
addOnetoArrayElement(myArray, 1);
console.log(myArray[1]); // Returns 3
I've been using JavaScript for years and this one has me stumped. As I understood things, when defining a var, one of two things will happen:
If the expression is a primitive, var is defined as a new instance of that primitive with no reference to the passed expression.
If the expression is an object literal, a new object is created.
If the expression is an existing object, var will reference the object and any future changes to the object will be reflected.
However, I've run into a situation where case 3 doesn't apply:
var obj = {body: {'a': 1, 'b': 2, 'c': 3}};
var ref = obj.body;
ref = JSON.stringify(ref);
console.log(typeof ref); // string
console.log(typeof obj.body); // object
Since ref is defined as the body property of obj, I thought redefining ref as a string also would affect obj.body. So what am I missing?
JSON.stringify is a method which takes an object and returns its string representation, it doesn't change anything. By doing ref = x you make ref point to another thing, it doesn't affect what was there before assignment.
That simply means, you are no more referencing obj.body.body and referencing to something else.
var ref = obj.body;
//ref holding obj.body now any changes to ref will effect obj.body.
ref = JSON.stringify(ref);
//ref holding a String returned by `stringify()` now any changes to ref will effect obj.body.
You see ?? You just changing the ref with different values. Not really changing anything on obj
Primitives are immutable. If there’s a difference in how they would behave compared to objects, you can’t observe that, so forget all that stuff about copying. Let’s talk instead in terms of “things”! Objects and primitives are both things. When you assign a thing to a variable, you are not copying the thing.
var x = literally any value;
var y = x;
x and y are both variables that contain the same thing. If you change the thing, it doesn’t matter where you access it from in the future; the thing changed. If you change the thing the variable contains, the thing it contained before is not affected.
var z = some other value;
y = z; // y now contains the same thing as z instead of the same thing as x
// only variables changed, and the things did not
There are a lot of answers that talk about this in other terms but I enjoy technical language.
tl;dr: For all intents and purposes, the distinction between objects and primitives in JavaScript is not a useful one.
ts;iwrse: This article about Python applies to JavaScript just as much.
Recently I started learning about object-oriented programming in JavaScript. What I understood, is that when referencing to variables, we in fact reference not to their actual values, but locations in the memory. That's why all those "return this" methods that are supposed to copy instances don't work.
So, example code:
//An example object with a simple property and
//failing "copy" function.
function MyObject()
{
this.myProperty = 123;
this.copy = function() { return this; };
}
var iOne = new MyObject();
var iTwo = iOne.copy();
iTwo.myProperty = 321;
Now "myProperty" property of both iOne and iTwo equals 321, because "copy" method returned a reference, instead of a value. This behavior is expected, and everything is okay.
Now, I tried doing the same with a native object type, Number. Let's create an instance of it, in a more object-oriented programmer-friendly way:
var iOne = new Number(123);
var iTwo = iOne; //Equals "iTwo = iOne.copy()", except there isn't a copy method
iOne = 321;
And now, something terrible happened. iOne equals 321, but iTwo kept its value and is still equal to 123.
I have no idea what is this behavior caused by. Maybe Number is some kind of "special"? Maybe the decimal number associated with it is something more than a property? Or maybe it's just supposed to make life of inexperienced programmers easier? The last option is related to operators. If anyone know something about it, please don't let my way of understanding JavaScript fall apart.
Objects, Arrays and Strings are assigned by reference (not by copy). All other types are effectively copies when assigned (e.g. they make a new variable that has nothing to do with the old one).
Strings are a special case because they are immutable so when you change a string, it always creates a new string so it behaves more like it makes a copy even though the previous assignment was a reference.
Assigning:
iOne = 321;
Is replacing the value of iOne with a simple primitive numeric type so it will have no effect on any other variable.
var iOne = new Number(123);
var iTwo = iOne; //Equals "iTwo = iOne.copy()", except there isn't a copy method
iOne = 321;
You're overwriting the object reference held by the iOne variable, with a distinct primitive number.
The objects are held as references, but they are not pointers that can be directly dereferenced, so you can't replace the data held in that memory location. You can only mutate it (if the object is mutable).
Specifically, the Number object wrapper is not mutable, or at least the primitive value it holds can't be replaced. You can only replace the entire object.
iOne = 321;
This code did what is expected, you assigned 321 to the variable iOne, overwriting what it was referencing to originally.
There's no real difference in behaviour between "native types" and objects in Javascript (except that native types are immutable).
In your second example you're simply changing what variable iOne is pointing to, why should it change what another independent iTwo variable is pointing to?
In the first case instead you have two variables pointing to the same object and if you use one variable to mutate the object and you can observe the change also using the other variable (obvious... it's pointing to the same object).
In Javascript you can imagine that everything is always by reference and never by value (copy). If you want to copy something you need to do it explicitly... for arrays you can use x.slice() to make a shallow copy of x; for objects there's no primitive function for doing the same so you must call the constructor.
A common OOP pattern is to have a member function .clone() that returns a copy so who needs the copy doesn't need to know how to make a copy of every class.
function P2d(x, y) {
this.x = x;
this.y = y;
}
P2d.prototype.clone = function() {
return new P2d(this.x, this.y);
}
Another possibility specific to the protoype model of Javascript and that can be useful in some cases is to create a separate object that will appear like a shallow copy that can be mutated without affecting the original but that is instead referencing the original object when reading:
function fakeCopy(x) {
function f() { }
f.prototype = x;
return new f;
}
p = new P2d(10, 20);
q = fakeCopy(p);
console.log(q.x); // Displays 10
q.y = 30;
console.log(q.y); // Displays 30
console.log(p.y); // Displays 20 -- original not changed
p.x = 99;
console.log(q.x); // Displays 99 (!)
This happens because Javascript objects have a "prototype chain" that is searched when accessing a member for reading. q is created as an empty object that with p as its prototype so when looking for an attribute (for reading) it will go searching inside p if something is not found inside q. When writing however an attribute will be set inside q, not affecting p and from that point on the value present in q will be returned instead of having to go up in the prototype chain.
I would like to understand well something i observe more and more.
In some circonstances, different instances from a same model change their attributes the same way (if i have 2 UserModel A and B, if i change A, B will be affected the same way).
I observed some practical cases:
When i send an instance of a model to a view constructor, they are linked, if i change the model in my view, the outside one will be affected the same way. I guess sometime we just send a pointer and not a copy of the instance to the view.
More specific with some code;
A = new UserModel();
B = new UserModel();
var Data = A.get('info'); //where info = {some: "thing"};
Data.some = 'other';
B.set('info', Data); //A.get('info') == B.get('info')
Because i got the object info and not only the attributes separately (i tested it and there is no repercution between the values this way).
So my question is, are we always using pointers with objects in javascript ? Is it specific to backbone ? I would like to understand what is behind this behavior.
Thanks.
Objects and Arrays are passed or assigned as references in javascript, not copies. If you want a copy of an object or an array, you have to explicity make a copy.
Simpler types such as numbers, boolean are copied when assigned or passed.
Strings are a bit of special case. They are passed as references, but since strings are immutable (can't be changed), you can't really use a string reference for anything because any attempt to modify the string creates a new string.
A couple examples:
// arrays assigned by reference
var a = [1,2,3];
var b = a;
a[0] = 0;
alert(b[0]); // alerts 0 because b and a are the same array
// objects assigned by reference
var c = {greeting: "hello"};
var d = c;
c.greeting = "bye";
alert(d.greeting); // alerts "bye" because c and d are the same object
// numbers assigned as copies
var e = 3.414;
var f = e;
e = 999;
alert(f); // alerts 3.414 because f is its own copy of the original number
// make a copy of an array
var g = [1,2,3];
var h = g.slice(0); // h is now a copy
h[0] = 9;
alert(g); // shows [1,2,3]
alert(h); // shows [9,2,3]
The same is true for passing arguments to a function or returning values from a function. Unless an explicit copy is created, arrays and objects are passed or returned by reference.
A shallow copy of an array can be made with the .slice() method.
var arr1 = [1,2,3];
var arr2 = arr1.slice(0); // make independent copy of first array
A shallow copy of an object can be made by copying each property from the original object to a new object.
Deep copies involve testing the type of each item being copied and recursing on the object if it is an object or array so nested objects and arrays are copied too.
While reading a book about JavaScript I stumbled across an example:
var names = new Array("Paul","Catherine","Steve");
var ages = new Array(31,29,34);
var concatArray;
concatArray = names.concat(ages);
My question is, why doesn't the variable concatArray need to be define as a new Array() in order to store the concatenated data for both arrays name and ages , but when I try to treat the concatArray as an array by adding another line of code "document.write(concatArray[0])", it works just like an array and shows me the data stored in the first element. I just wonder why I'm not declaring the concatArray as a new array, yet it still works as one.
You are declaring concatArray as a new array but the declaration is implicit. The concat function returns a new array which contains concatenated copies of the original two arrays. The type of concatArray is inferred from the return type of the concat function.
Variable don’t have a specific data type in Javascript like in other languages. You can assign a variable every value you want.
That means var concatArray; declares the variable but the value is undefined:
var concatArray;
alert(typeof concatArray === "undefined");
Only when assigning the return value of names.concat(ages) (an array) to concatArray it get’s that type:
var names = new Array("Paul","Catherine","Steve");
var ages = new Array(31,29,34);
var concatArray;
alert(typeof concatArray === "undefined");
concatArray = names.concat(ages);
alert(concatArray.constructor === Array);
Javascript doesn't care what the contents of the var are when it is declared; that is why you can declare var concatArray without needing to specify it as an array. Once you assign it a value and a type (as the result of the concat() function) javascript treats the var as an array.
Simply put, w3schools says it pretty concisely:
The concat() method is used to join two or more arrays.
This method does not change the existing arrays, it only returns a copy of the joined arrays.
w3schools
Looks like Andrew and Matthew beat me to it anyway.
Because Javascript is dynamically typed. A variable doesn't have a specifuc type, and an array is an object that you can assign to any variable.
When you declare a variable without assigning it a value, it just exists with an undefined value:
var answer;
// now the variable exists, but it doesn't have a value
answer = 42;
// now the variable has the numerical value 42
answer = "hello";
// now the numerical value has been replaced with the string value "hello"
answer = [];
// now the variable contains an empty array
answer[0] = 1337;
// now the variable contains an array that contains an item with the value 1337
answer = -1
// now the array is gone and the variable contains the value -1
I would make an answer slightly different of Andrew's one.
JavaScript variables are not strongly typed. You can put a string, then a number, then an object in the same variable. When you use the variable, the interpreter checks its current type is suitable for the usage you try to make. If you write:
var a = 45;
alert(a[0]);
a = [ 5 ];
alert(a[0]);
you will get successively undefined then 5.