58

Basic JavaScript question: Since there is no hard limit for arrays as the case with Java (i.e. IndexOutOfBoundsException), what is the use of the declaration where we specify the length property?

var a = new Array(10);

I know it predefines the length and puts "undefined" into those empty spots. Is that reason enough for having it?

repeat
  • 18,496
  • 4
  • 54
  • 166
OpenSource
  • 2,217
  • 2
  • 21
  • 22
  • 4
    This is obviously an old post and so behaviour may have changed, but i think it's worth clarifying that the slots aren't filled with `undefined`, they're actually empty (i.e `[,,,,,]`). See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of (Note: this implies an array of 7 empty slots, not slots with actual undefined values). – james Apr 13 '18 at 11:09
  • 5
    This subtle difference means that although the new Array has the specified length, you can't actually use any of the Array iterator methods to iterate over that array. i.e. `var a = new Array(5).map((value, index) => index);` does not set `a` as `[0, 1, 2, 3, 4]` it remains as `[,,,,,]`. So although `fill`, `filter`, `every` etc work as expected, it's easy to get caught out by the fact that `map`, `forEach` etc do not. – james Apr 13 '18 at 11:15
  • 2
    A solution to this is to use `Array.from(Array(10)).map((arg, index) => index)` – james Apr 13 '18 at 11:25
  • This is often used by C# programmers who are confused about Javascript's Arrays and think this will fix the size of the array. – Quentin 2 Jun 20 '18 at 15:22
  • 1
    @james I agree with your first comment. I think it should be noted that they aren't filled with `undefined`. – Giorgi Moniava Aug 12 '18 at 20:39
  • performance of adding elements is better when you pre-allocate your arrays - case in point check this repo: https://github.com/mchaov/combinations – Martin Chaov Aug 02 '19 at 12:27

9 Answers9

42

There are many perceived benefits of declaring an array size, but I think the majority of the perceived benefits are just FUD being passed around.

Better performance!/It's faster!

As far as I can tell the difference between pre-allocating and dynamic allocation is negligible.

More interestingly, the spec does not state that the array should be set to a pre-allocated length!

From Section 15.4.2.2 ECMA-262:

If the argument len is a Number and ToUint32(len) is equal to len, then the length property of the newly constructed object is set to ToUint32(len). If the argument len is a Number and ToUint32(len) is not equal to len, a RangeError exception is thrown.

An unscientific for-fun test case is here: http://jsbin.com/izini

It makes for more understandable code!

Personally, I disagree.

Consider the javascript you have written in the past, and consider code you may have to write in the future. I can't think of a single time where I've needed to specify a static limit on one of my arrays. I'd also argue that the potential problems of limiting arrays in javascript highly outweigh the benefits caused by letting people know what you were thinking with no actual checks behind it. Lets weigh the pros and cons...

Pros:

  1. It will be easier for them to understand what you intended the code to do.
  2. They will be able to find the bugs caused by your assumption later on (tongue firmly in cheek)

Cons:

  1. Quick glances can easily confuse "new Array(10)" with "new Array('10')" which do entirely different things!
  2. You are imposing an arbitrary limit on code with no normal length limit causing you to write lots of boiler plate code to check and maintain the limit.
  3. You are imposing an arbitrary limit on code which could probably have been generalized to work with any length of values.
  4. You're making an assumption about how people will read your code while assuming that the alternative would be less confusing.

You may as well have written:

//I assume this array will always be length 10
var arr = new Array();

In the above case the comment might even be preferable. The explicit declaration of intent can avoid any confusion not used to using the constructor as a declaration of intent.

Fine then.. why do you think it's even there then?

Convenience. When they were writing the spec I think they realized two things.

  1. This sort of assignment would be something developers coming from similar languages would be used to.
  2. Implementations of ECMAScript might potentially use it for performance gains.

So they put it in there. The spec only defines the use of the parameter, not how it should be implemented.

coderjoe
  • 11,129
  • 2
  • 26
  • 25
  • 1
    I think the only thing I found that it's really very useful for is to create a string repetition function: `function(s,n) {return Array(n+1).join(s);}` – Codesmith Nov 22 '12 at 02:11
  • 1
    http://gamealchemist.wordpress.com/2013/05/01/lets-get-those-javascript-arrays-to-work-fast/ has some great performance explanations. – rustybeanstalk Feb 11 '14 at 01:23
  • 1
    It's also useful for stuff like: `palette8Bit = new Array(256); palette4Bit = new Array(16);` ... which I would argue absolutely DOES improve code clarity, even if JS prevents rigorous enforcement of that dimension without extra nonsense. – Dewi Morgan Aug 29 '15 at 00:21
  • 1
    @DewiMorgan you're not wrong. The javascript landscape has changed significantly since this post was originally authored (almost 6 years ago exactly... WOW!). I wouldn't be surprised if the various javascript VMs were optimized for these cases now too. I still argue that the "improved readability" aspect of the array is still largely a factor of the type of team you're on and the javascript coding standards they use but... well... you're definitely not wrong. :) – coderjoe Aug 31 '15 at 19:28
  • 1
    The bin posted does not actually do the benchmarking properly. I do not know if JS Bin has changed since, but the loops were being prematurely exited as they were running too long. This updated version works as expected and shows the difference as much more significant than the linked one: http://output.jsbin.com/tikivikora – dawsonc623 Jul 20 '16 at 16:32
  • 1
    This one shows the differences: https://jsbin.com/rufolunaya I've also added "allocated but filled from the end" as the results are VERY different. In Chrome preallocation matters, also push() is faster then changing length through setting new elements. In Firefox you may see it ignores it and the reverse fill is the really bad thing to even try. IE11 doesn't preallocate and doesn't implement "push" well. – tskala Dec 12 '18 at 13:01
  • 5
    This answers is ancient and totally false. In Node, I can initialize an array `const s = new Array(10000000)` and do 10 million inserts and with the array literal `[]` it takes 23.6 seconds and with the constructor, 5.5 seconds. – Cazineer Aug 17 '19 at 05:52
  • " I can't think of a single time where I've needed to specify a static limit on one of my arrays". I'll give you an use case: Imagine that you want to unsort an array. You know the length of the output array in advance and you will have to add all the old items in the new unsorted array. If you don't specify fixed length, you'll be increasing dinamically the size n times. – Eric Jan 02 '20 at 15:45
  • The bin posted by coderjoe is just wrong why using arguments.length instead of length ? @dawsconc623 fixed it but still use arguments so pull undefined value ???? if the test is replaced with 0 instead of arguments[...] then preallocated version is way faster so coderjoe please fix your post. – Et7f3XIV Sep 12 '22 at 09:40
33

Performance on the V8 JavaScript engine.

By doing:

var arr = []; arr.length = 1000;

V8 will preallocate the required memory for the array and maintain/set the array's Hidden Class to compact SMI (Small Int, 31 bits unsigned) array. However, this is not true when the desired length is too big, which results in the HC being set to sparse array (i.e., map).

Try the following link on Chrome: http://jsperf.com/0-fill-n-size-array

I've included an extra test case without the array length definition so you can tell the actual performance difference.

Related info: http://www.youtube.com/watch?v=UJPdhx5zTaw

Gabriel Garcia
  • 1,000
  • 10
  • 11
13

Clarity.

When writing code, your goal is not so much for the computer to understand you, but for the next programmer that reads your code to understand you.

var xs = new Array(10);

The above code shows your intention: to have a 10 element array.

var xs = [];

The above gives nothing away; no extra information.

Cheers.

scvalex
  • 14,931
  • 2
  • 34
  • 43
  • 9
    var xs = []; tells you that number of items which will be stored in this array is not fixed. [It could be dependent on user input for example] – SolutionYogi Aug 07 '09 at 19:11
7

I am not sure, but I would bet it allocates memory differently at a low level. If you know you're creating 10,000 items, just reserve that much space rather than dynamically having it resize it in the background all the time.

Max Schmeling
  • 12,363
  • 14
  • 66
  • 109
4

Suppose you want an array of a certain length initialized to a certain value. This will not work:

scores = [].fill(0.0, 0, studentCount);

It'll just give you an empty array, because fill() will never extend the array beyond its original length.

This will work:

scores = new Array(studentCount).fill(0.0, 0, studentCount);

It'll give you an array of studentCount values initialized to zero.

bpreece
  • 41
  • 1
2

I've created this JSPerf which demonstrates the problem, including it's various versions. Arguments I find are such:

  1. Using new Array() can behave unexpectedly, can be overridden
  2. Setting .length doesn't actually increase the array size in some browsers
  3. The performance hit isn't really there.

I think these tests should put those arguments to rest, but it should be noted that different browsers treat this problem very differently. Firefox seems to optimize and figure out how large the array will be, while Chrome allocates memory as one would expect. And, as usual, Internet Explorer just stinks at the task.

FesterCluck
  • 320
  • 1
  • 7
0

Well, personally i want a queue. I want the queue to be length of 10.

The easiest way to do this is to use the push array method to put items onto the end of the queue, and the shift() method to get them off the front of the array.

The problem is, if i want to make a simple "add" method to my queue, and i write it up like so:

function addItemToArray(item, array){
    array.shift();
    array.push(item);
    return array;
}

then Nothing Good happens. What is better (actually, what will work) is to declare my array like this:

var maxSizeIsTen = new Array(10);

and then use it everywhere. Also, note the "nice" way of describing the array - no comments that nobody reads, and anybody using this code will work out in short order the max size of this array.

YAY!

bharal
  • 15,461
  • 36
  • 117
  • 195
  • I don't know what you expect this to do. Your `maxSizeIsTen` array can contain more than 10 elements. In many other languages if you try to add more elements to a fixed-size array you'll get an error; an element won't disappear as I gather you want it to. – ZachB Mar 17 '13 at 01:11
  • huh. i was scratching my head wondering what i was on about here - i think i have it, but didn't explain it neatly in the answer. IF you are using an array for a queue, AND you want the queue to have a fixed size, then you will want to use the `new Array(someSize)` command. I suppose you could sit and write "undefined" a whole bunch of times, but that's stoopid. In the answer, i'm suggesting that IF you are using a similar method as `addItemToArray` to maintain the queue, then you *need* to have the array size preset (or you just have a queue of size one). – bharal Mar 18 '13 at 04:14
  • Maybe, but I can't think of any situations where it's preferable to have a bunch of undefined elements that you'd have to `shift` through and have your downstream code know how to handle. Call `push` on your blank array 10 times and you have an array of length 10. – ZachB Mar 18 '13 at 15:06
  • 1
    Ho! Circular array used as a queue. You care less for what it is full of - you know what the entry point of the queue is. That's actually how i came on this question anyway, i had to implement one for performance reasons and was looking for the syntax. – bharal Mar 18 '13 at 15:40
0

Its not hard to maintain array size. Take a look on following example :

function updateArray(){
    var a = [1,2,3,4,5,6,7,8,9,10]; //original array object
    var b = [11, 12, 13];  //New array object to be pushed
    a.splice(a.length-b.length, a.length);
    a.unshift.apply(a, b);
    return a;
}

a.unshift.apply(arg1, arg2) push the new element on top and a.push.apply(arg1, arg2) at bottom.

Vish
  • 832
  • 7
  • 21
0

As all of the answers with actual benchmarks no longer load, I created a new benchmark: https://jsbench.me/hhlk3jgvu4/1

The results on Chrome on macOS as of July 2023 are:

1. fillArrayWithSetLen          19K ops/s
2. fillArrayWithLenConstructor  18K ops/s ( 2.87% slower)
3. fillArrayWithLiteral         17K ops/s (11.71% slower)
4. fillArrayWithPush            15K ops/s (20.36% slower)

A second run with a randomized order yielded the same relative ranking:

1. fillArrayWithSetLen          19K ops/s
2. fillArrayWithLenConstructor  19K ops/s ( 1.98% slower)
3. fillArrayWithLiteral         16K ops/s (15.58% slower)
4. fillArrayWithPush            16K ops/s (19.85% slower)

Code in case the benchmark site disappears:

const testLen = () => {
  const rand = Math.random();
  return rand < 1 ? 4192 : rand;
}

const fillArrayWithPush = (len) => {
  const ar = new Array(len);
  for (let i = 0; i < len; i++) {
    ar.push({x: i, y: i + 1, z: i + 2, str: `str${i}`});
  }
  return ar;
};

const fillArrayWithLenConstructor = (len) => {
  const ar = new Array(len);
  for (let i = 0; i < len; i++) {
    ar[i] = {x: i, y: i + 1, z: i + 2, str: `str${i}`};
  }
  return ar;
};

const fillArrayWithLiteral = (len) => {
  const ar = [];
  for (let i = 0; i < len; i++) {
    ar[i] = {x: i, y: i + 1, z: i + 2, str: `str${i}`};
  }
  return ar;
};

const fillArrayWithSetLen = (len) => {
  const ar = [];
  ar.length = len;
  for (let i = 0; i < len; i++) {
    ar[i] = {x: i, y: i + 1, z: i + 2, str: `str${i}`};
  }
  return ar;
};

Joe
  • 3,370
  • 4
  • 33
  • 56