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
Related
The following code example is what i found here on the internet
arr[1].push('native', 'laravel');
with a description of
Here we use the javascript array push method to add two new elements(items) to the inner sub-array.
which was exactly what i wanted to accomplish!
But when i tried it several times by my self it always went like this:
let mainArr = [];
console.log("mainArr: ", mainArr);
// Working as expected: created an empty array
let emptyArr = [];
console.log("emptyArr: ", emptyArr);
// Working as expected: created an empty array
for (i = 0; i < 3; i++) {
mainArr.push(emptyArr);
}
console.log("mainArr: ", mainArr);
// Working as expected: pushed 3 times the "emptyArr" into "mainArr"
let newArr = [["X"], [90]];
mainArr[0].push(newArr);
console.log("mainArr[0]: ", mainArr[0]);
console.log("mainArr[1]: ", mainArr[1]);
console.log("mainArr[2]: ", mainArr[2]);
// NOT working as expected: pushed "newArr" into every of the three arrays within the "mainArr" !!!
// How can I solve this?
Would love to hear some tips :)
Thanks!
That's because arrays are objects in JS, ie non-primitive type of variables.
Long explanation
Javascript has 5 data types that are passed by value: Boolean, String, Number, null and undefined. These are called primitive types.
And it has 3 data types that are passed by reference: Array, Function, and Object. These are all technically Objects.
When you assign a non-primitive value to a variable what happens is that a reference of that value is assigned, not the actual value. Ie, that reference points to the object’s location in memory. The variables don’t actually contain the value.
In your case
When you push emptyArr 3 times you don't insert the value (ie. a new empty array) you insert the same memory reference of emptyArr. These 3 references point to the same location in memory that has that value of an empty array. Given that, now you insert newArr to the referenced value of the mainArr[0], but the rest elements mainArr[1], mainArr[2] are references that point to the same value as the one pointed by mainArr[0].
Hope it makes sense.
Otherwise, You can refer here
https://stackoverflow.com/a/430958/4700560 or I also find this article very visually descriptive and helpful https://blog.penjee.com/passing-by-value-vs-by-reference-java-graphical
This question already has answers here:
Understanding Javascript callback parameters
(3 answers)
Closed 5 years ago.
var animals = ["cat","dog","fish"];
var lengths = animals.map(function(c) {
return c.length;
});
console.log(lengths);//[3, 3, 4]
Here is the code. I don't understand where this 'c' argument comes from.
I tried to change this argument to another one (any word, actually, in both places), and the console.log result is always the same!
But this 'c' is not defined anywhere! Where does 'the engine' get the value of this 'c'?
You've asked two slightly different questions. First to the question body:
I don't understand where this 'c' argument comes from. I tried to change this argument to another (any word, actually, in both places), and the console.log result is always the same!
But this 'c' is not defined anywhere!
Where does 'the engine' gets the value of this 'c'?
You define the parameter name (as you've noticed, you can choose any name for it you like). The value comes from the array, because map calls your callback and determines what argument to pass for that parameter.
Here's a conceptual implementaton of Array.prototype.map, which make make this clearer:
// CONCEPTUAL ONLY, NOT AN ACTUAL VERSION OF IT
function maplike(array, callback) {
var result = [];
for (var i = 0; i < array.length; ++i) {
result[i] = callback(array[i]);
// ^^^^^^^^--- where 'c' comes from
}
return result;
}
var animals = ["cat","dog","fish"];
var lengths = maplike(animals, function(c) {
return c.length;
});
console.log(lengths);//[3, 3, 4]
Do array elements have names by default in JavaScript?
Sort of, but not in the way you're thinking. The name of the element is its index, 0, 1, etc. In fact, JavaScript arrays aren't really arrays at all* and those indexes are converted to string property names (in theory; in practice, JavaScript engines optimize it).
* (disclosure: that's a post on my anemic little blog)
You're telling the interpreter how the parameter is called, here:
function(c) {
^
Array.prototype.map() requires a callback that accepts up to 3 parameters. The first parameter is always the "current item", which you happen to have named c.
For a more in-depth explanation, have a look at T.J. Crowders answer, as well.
In javascript, functions are first class object, which means they can be assigned to variables, passed as function parameters and returned from values. The Array.prototype.map function takes a function with it's first parameter denoting an item of the array. When invoked, the map function executes the given function for each of the items and creates a new array from the outputs of the given function.
In your case, you are defining the input function on the fly, inside the map function.
You can actually define the function outside and pass the function by reference inside map like below.
function getLength(item) {
return item.length;
}
var animals = ["cat","dog","fish"];
var lengths = animals.map(getLength);
console.log(lengths);//[3, 3, 4]
Here, you can see it outputs the same result.
The code does not know what is the parameter named. You map an array. map function creates a new array in the lengths variable (variable being assigned to). How? It provides to the function parameter inside it, each element in the current array one-by-one by value.
Here the value is actual string name ("cat" or "dog" or "fish").
In javascript, parameters can be optional. This map function can take three parameters, currentValue, index, array. In your case, c provides currentvalue.
If you would add one more parameter c,idx. Map function will get currentvalue and index inside it.
var animals = ["cat","dog","fish"];
var lengths = animals.map(function(c, idx, arr, test) {
console.log(c); // currentvalue being processed in the array.
console.log(idx); // index of currentvalue in the array
console.log(arr); // original array being operated on.
console.log(test); // undefined always. not available in map.
return c.length;
});
console.log(lengths);//[3, 3, 4]
This question already has answers here:
JavaScript "new Array(n)" and "Array.prototype.map" weirdness
(14 answers)
Why does Array.apply(null, [args]) act inconsistently when dealing with sparse arrays?
(2 answers)
Difference between Array.apply(null, Array(x) ) and Array(x)
(5 answers)
Closed 5 years ago.
I've recently discovered that mapping an uninitialised array doesn't seem to work as I would expect. With this code:
function helloMap(value, index) {
return "Hello " + index;
}
console.clear();
var initialArray = Array.apply(null, new Array(5));
console.log("Array initialised with apply:");
console.log(initialArray);
console.log(initialArray.map(helloMap));
var normalArray = new Array(5);
console.log("Array constructed normally");
console.log(normalArray);
console.log(normalArray.map(helloMap));
.as-console-wrapper {
max-height: 100% !important;
}
I get different results despite the first output for each array being [undefined, undefined, undefined, undefined, undefined].
The fact that I get different results implies that the undefined in these 2 arrays are in fact different. In the first I suspect that the array has 5 items in, each one is undefined. In the second the array is 5 items long but there is nothing, not even undefined in there...
It's a bit confusing.
Can someone explain it to me?
Array.apply(null, Array(5)) actually fills the array (or array-like object) that you pass as the second argument with the value of the first argument you pass in, as can be seen in the MDN Docs.
new Array(5) is just initializing an array with it's length property set to the argument of 5. Again, as can be seen in the MDN docs:
If the only argument passed to the Array constructor is an integer between 0 and 232-1 (inclusive), this returns a new JavaScript array with its length property set to that number (Note: this implies an array of arrayLength empty slots, not slots with actual undefined values).
According to MDN Array.prototype.map
map calls a provided callback function once for each element in an
array, in order, and constructs a new array from the results. callback
is invoked only for indexes of the array which have assigned values,
including undefined. It is not called for missing elements of the
array (that is, indexes that have never been set, which have been
deleted or which have never been assigned a value).
Both the arrays are different in the way Array.map executes the callback.
Since the array in second scenario doesn't have indexes map is returning empty
REASON
The answer lies in Array constructor
In the first scenario you are passing an array with length 5 to Array constructor which will index the array based on the length but in the second scenario you are just using the array with length 5
You will get to know the difference when you run Object.keys(initialArray) with Object.keys(normalArray)
Try checking the below example.
function helloMap(value, index) {
return "Hello " + index;
}
console.clear();
var initialArray = Array.apply(null, new Array(5));
console.log("Array initialised with apply:");
console.log(initialArray);
console.log(initialArray.map(helloMap));
var normalArray = new Array(5);
console.log("Array constructed normally");
console.log(normalArray);
console.log(normalArray.map(helloMap));
//DIFFERENCE
console.log("Initial Array: "+Object.keys(initialArray));
console.log("Normal Array: "+Object.keys(normalArray));
.as-console-wrapper {
max-height: 100% !important;
}
map is not called on any elements of normalArray because there are no elements. Normal array is an array with 5 empty slots.
initialArray has 5 slots filled with undefined
I think here is your answer, mozilla developers
console.log(normalArray.map(helloMap)); will always fail, because it runs in the context - creates new array but won't work with undefined values.
map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results. callback is invoked only for indexes of the array which have assigned values, including undefined. It is not called for missing elements of the array (that is, indexes that have never been set, which have been deleted or which have never been assigned a value).
This question already has answers here:
JavaScript by reference vs. by value [duplicate]
(4 answers)
Closed 6 years ago.
I want to use = to initiate an array in a function. However, = cannot change the array, but push can.
I want it equals to ["a", "b"]. But now the result is ["1", "2"].
I tried arr = ['a', 'b']; and also arr = ['a', 'b'].slice();. But neither works.
How can I let = work in this case?
var array = [];
init(array);
console.log(array);
function init(arr) {
arr.push('1');
arr.push('2');
arr = ['a', 'b'];
}
https://jsbin.com/kiveyu/4/edit?js,console
So the reason this happens is because you are assigning the local variable the new array, whereas prior to the assignment, the local variable held the value of the passed in array.
The parameter holds a reference to the value passed in. However, the parameter is still a local variable. Writing to that variable will only modify the local variable, and will lose the reference held.
To expand, from being called:
init(array);//call function init with value array
Next the context environment is created upon instantiation, it holds a local variable array, and the value of that variable is the same as the value of the passed in array (in your example they have the same name)
function init(array) {
After this, two values are pushed to the value of array, which is the value of the passed in array.
array.push('1');
array.push('2');
Here is where it seemed the confusion took place. The local variable array (still holding the value of the passed in array) has its value changed to a new array. The result is that the local variable array no longer holds the value of the passed in array, but now holds the value of ['a','b'].
array = ['a', 'b'];
That is why it looks like you cannot change the array by assignment - because the only thing you have access to in that scope is the local variable with regards to the original array.
function init(array) {
array.push('a');
array.push('b');
}
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.