Recently i was experimenting with array push() method in javascript.
I created an array, and pushed the same array using push method.
var a=['hello', 4]
a.push(a)
the result was surprising, when i explored the array items in chrome console, there was infinite array produced. i want to know why this is happening. As the push() method adds the element on last.
I was expecting this result
['hello, 4, ['hello',4]]
but the result was something else, here is the screenshot of chrome console
problem screenshot, infinite inner arrays using push method in JavaScript
When you assign an object to a variable in Javascript (and indeed in most similar languages), that variable holds what is called a "reference" to that object. Note that arrays in JS are objects, but primitive values like strings and numbers are not.
One consequence of object assignments being "by reference" is that any change you make to that object - even if its done to another variable that happens to reference the same object - will "show up" when you inspect the original variable.
So here, you start off with
var a=['hello', 4]
And then do
a.push(a)
then the object (array) to which a points has been changed. It now has an additional element on the end - which is a, the very same array we're talking about.
So it expands recursively:
a = ['hello', 4, a]
= ['hello', 4, ['hello', 4, a]]
= ['hello', 4, ['hello', 4, ['hello', 4, a]]]
...
And so on, infinitely. This doesn't require an infinite amount of memory, because the third element of the array is simply a reference to the memory location which holds the array.
Related
Suppose I made a 5 by 2 matrix populated by integer 9
let arr = Array(5).fill(Array(2).fill(9));
Now I want to push integer 8 into the fourth line of the matrix by doing
arr[3].push(8);
I ended up getting [[9,9,8],[9,9,8],[9,9,8],[9,9,8],[9,9,8]]. All lines in the matrix have been pushed 8.
Why?
I found out if I did things like below, I got what I wanted, but why?
arr[3] = [...arr[3]];
arr[3].push(8);
Short Answer: This is because all 5 arrays are sharing a reference to the exact same array
i.e. Array(2).fill(9) => [9,9]
Quick Solution: Use Array.form to create a deep-copy for each layer
let arr = Array.from({length: 5}, e => Array(2).fill(9));
or try any other solution from these answers (ref 1, ref 2, ref 3)
See the working solution below (notice the shallow copy in the console)
// PROBLEM
// Shallow-copies
let arr1 = Array(5).fill([...Array(2).fill(9)]);
arr1[3].push(8);
// writing to the document
document.write('Shallow -> ', arr1)
// loggin in console
console.log('shallow ', arr1)
// SOLUTION
// deep-copies
let arr2 = Array.from({length: 5}, e => Array(2).fill(9));
arr2[3].push(8);
// writing to the document
document.write('<br><br> Deep -> ', arr2)
// loggin in console
console.log('deep', arr2)
Details: As mentioned in the documentation here (5th point in Description here) about the first parameter of .fill which is the array you are passing in i.e. [9,9]
If the first parameter is an object, each slot in the array will reference that object.
Exact scenario is mentioned inside the documentation as well, see this code snippet here
This shows that all the items are basically a reference to the same object [also known as shallow copy], altering one item will result in the same changes in every other item.
Array.from fixed this issue as Array(2).fill(9) will now create a new array on every element of the array. Learn more about Array.from here
This fix you mentioned at the end is working because by using spread operator [...arr[3]] we are creating a deep copy of this [9,9] array on the 4th index, which is not a reference to that same initial array [9,9] anymore so, now changing this item at 4th index will only change the 4th item's array itself.
Note This spread operator ...arr is only creating a single level deep copy so, if (supposedly) this arr[3] contains another multi-level array inside it, then only the first level will be created as a deep copy and all the inner ones will remain as shallow copies [i.e. same reference to the object]
Doing Array.apply(null,[1]) gets me this [empty] but Array(null,[1,2]) gets me [1,2]
the array with 'empty' has a length of one but index zero is undefined.
Array.apply(null,[1]).length
1
Array.apply(null,[1])[0]
undefined
Array.apply(null,[1])
[empty]
console.log(Array.apply([1]));
console.log(Array.apply([1]).length);
There is nothing like empty variable in Javascript, they are empty slots. You are calling Array([1,2]) instead Array.apply([1,2]).
Both Array.apply([1]) and Array.apply([1,2]) will give you same result which will be [].
But when you call Array.apply(this,[1]) it will result in [empty] because internally it is equal to Array(1). And when you call Array.apply(this,[1,2]) it is equal to Array(1,2) which will be [1,2]. When only one number passed to Array constructor it returns an array of empty slots with its length property set to that number.
MDN Docs
The reason you are getting two different results is that in the first case of doing:
Array.apply(null,[4])
You are simply invoking the JavaScript Array constructor with a list of one argument. This single argument to the constructor is used to create an array of size n where n is the number you have in the list. It creates this list without anything in it.
For me in a node REPL, this is the result of the above:
> Array.apply(null, [4])
[ <4 empty items> ]
In the second case where you did:
Array.apply(null, [1, 2])
This is another constructor overload used to specify the contents of the array. So in this case, you are telling the array constructor to create an array containing the elements 1, 2.
> Array.apply(null, [1, 2])
[ 1, 2 ]
See the MDN documentation for details.
The explanation for a dense array that I read from a few pages seem to be in contradiction to one another. I'd like some help understanding what it is.
While some links (search result 1, search result 2) suggest that it simply is an array where:
the elements of the array are known to be specific values; and
are assigned to the array at the time of its initialization.
The allusion there is that JavaScript arrays are dense.
It all makes sense up until here.
But this statement taken from the JavaScript Guide on the Mozilla Developer Network (MDN) says:
Since an array's length can change at any time, and data can be stored at non-contiguous locations in the array, JavaScript arrays are
not guaranteed to be dense; this depends on how the programmer chooses
to use them. In general, these are convenient characteristics; but if
these features are not desirable for your particular use, you might
consider using typed arrays.
And this has confused me now. My question is:
What does the statement on the MDN page mean when it says JavaScript arrays are not guaranteed to be dense? If it means that the following is not a dense array because one or more of its elements are undefined at the time of initialization, then why do the links I listed above seem to indicate that JavaScript arrays are indeed dense?
var array = new Array(1, , 3, ); // [1, undefined, 3, undefined]
"Dense" is in opposition to "sparse", and generally is used when talking about storage. For example, this array is dense:
a = [undefined, undefined, 2]
It can be stored in memory exactly like that: a sequence of three locations, the first two being undefined, the third being 2.
This array is sparse:
a = []
a[100000000] = 100000000
It is not stored in memory as a sequence of 100000001 locations, as it would be horribly inefficient. It is definitely not 100000000 places of undefined followed by 100000000. Rather, it just says 100000000th one is 100000000, and there is no space allocated to the first 100000000 elements.
(Actually, try to do this with 2 instead of 100000000, and you'll notice a curious thing: Chrome will display the dense array as [undefined, undefined, 2], but the sparse one as [undefined × 2, 2].)
Those articles say you can create an array being dense. This means that, at the time of creation, such arrays are dense, since in arrays like:
var a = new Array("foo", "bar", "baz");
var b = [2, 3, 5];
every element is set: from 0 to length-1, there is no undefined value. Or better said: every position from 0 to length-1 was assigned a value (even if the value is, actually, undefined).
However, you can make those arrays not dense anymore, by doing something like this:
a[20] = "bat";
That array, which was dense, is not dense anymore since the elements 0 1 2 and 20 (unlike elements in 3 to 19) are set to a value (this array has 4 elements, not 21).
When I access arguments from inside called function in nodejs...
echo '!function(){ console.log(arguments) }(1,2,3)' | node
... it outputs an object ...
{ '0': 1, '1': 2, '2': 3 }
... but in google chrome ...
!function(){ console.log(arguments) }(1,2,3)
... outputs ...
[1, 2, 3]
Why is it not consistent? What other js environments have this different behaviour? Is it ok for me to use arguments? How can I ensure it is always an array before I use it?
There are no rules for console output. The output doesn't represent anything that has to do with the language standard.
If you create a program that logs values from within a JS program, you're free to display those values however you want.
The arguments object is the same in both places. Only how it's being displayed is different.
How can I ensure it is always an array before I use it?
It's never an array. It's always an array-like object. In both cases its showing a value at object members 0, 1 and 2. Just uses different display syntax to do it.
In Chrome, try creating an object like this:
var my_obj = {
"0": 1,
"2": 3,
"1": 2,
length: 3,
splice: function(){},
};
And the log it in the console. It'll probably look like an Array literal, though it's obviously not.
It's not an Array in the browser, either. The arguments object has actually been in JavaScript longer than Arrays have, and when Arrays were first added to JavaScript (back in its 1.1 days), they didn't update arguments to be an Array. They haven't done it since then either, because of worries about backward-compatibility. It shows up in some browser consoles as though it were an Array because the debug console knows that most people plan to treat it as one anyway.
But the arguments object is close enough to an Array that it's easy to convert. One way to do it is like this:
var args = Array.prototype.slice.call(arguments, 0);
Array.prototype.slice, like most of Array.prototype's methods, is written to be generic: you can call it on Objects that aren't Arrays, and as long as they have properties with numeric names and a length property that's a Number, it'll work. The arguments object has both of these things, so these functions will work on it.
The slice function returns an Array which is a shallow copy of whatever object was passed into it, starting and ending at whatever numbers you specify. We tell it to start at zero, and because we didn't say where to stop, it assumes we want it to stop at the end, wherever that might be. This way, we copy all of the elements of arguments into a new Array, and now you can do whatever other Array things you want with it.
I have a javascript array of objects that are each created with 'new'. In the event of an error I would like to clear the whole array so that it would be GC'ed by the JS engine. To this end is it sufficient to just set the array variable to 'null' or do I need to splice all the elements from the array and set them to null before setting the array variable to 'null'?
The reason I am asking is that in Firefox I displayed (console.log) the array before assigning it to null and the displayed object (which is usually updated in the display I assume even later) still shows the elements of the array when I inspect it later, hence my doubt if the elements are actually being free'd or not.
To clear an array you can just set the length to zero:
var arr = [1,2,3,4,5];
console.log(arr);
arr.length=0;
console.log(arr);
Result:
[1, 2, 3, 4, 5]
[]
Edit:
Just found this on the topic: http://davidwalsh.name/empty-array
Looking at the comments it seems that just setting the variable to a new array is the simplest method:
arr = [];
According to the test results the memory is GC'd quicker than setting the length to 0. I would imagine this is to do with the allocations triggering the GC.
There is an interesting performance test on various methods here: http://jsperf.com/array-destroy