Last Updated: March 21, 2023
·
158.4K
· jjperezaguinaga

Why never use new Array in Javascript

I love javascript, but sometimes it seems it doesn't love me back. Take for instance the Array constructor function.

var a = new Array(1,2,3,4,5);
a[0] // returns 1
a.length // returns 5

Wanna take a shot in what this returns?

var a = new Array(10);
a[0] // returns undefined
a.length // returns 10, because of reasons.

This only happens when you give only one integer to the Array constructor. Why, what it's doing? Well, the new Array constructor is taking a page from some programming languages where you needed to specify the memory for your array so you don't get those ArrayIndexOutOfBounds Exceptions.

int *a = (int *) malloc( 10*sizeof(int) ); // ya ol' c
int *a = new int[10]; // c++
int[] a = new int[10]; // java

Yep, it's actually creating an array of with length of 10. We don't have a sizeof function in Javascript, but a toString is enough to prove it.

a.toString() // returns ",,,,,,,,,", a comma for each element + 1

You may think that it is correct "semantics", and it is. How else would you create an array of X length? The catch here is, that I've never needed to define a size in an array, since Javascript doesn't need to allocate memory for an array. They are more like... ArrayLists, if you want to go all Computer Science on it. For instance, all the following code is valid in Javascript:

a.pop(); // returns last object of array, removes it
a.push(20); // puts 20 as the last element of the array, returns length
a.shift();  // returns first object of the array, removes it
a.unshift(30); // puts 30 as the first element of the array, returns length

Using new Array[int] doesn't really have any sense, use, and can prompt to confusion in Javascript. Why? Because native methods of javascript think you know this. For instance, imagine this.

var a = new Array(10);
a.push(30); // returns 11, which if it were a proper stack/queue it would return 10
a.toString() // returns ",,,,,,,,,,20", probably not what you want

We should always use the [] constructor to avoid this kind of things.

var a = [10]
a.toString() // returns "10"
a[0] // returns 10
a.length // returns 1

Am I thinking interview question here? Ask a java/c programmer to create an array of X size and then push elements to it. Just don't be a jerk and ask questions like "What does {} + [] returns?". That's just mean.

18 Responses
Add your response

I smiled at the last paragraph :)

Actually it appears that in JavaScript, in 99% of cases you don't need to use constructors for any built-in type (e.g. Array, Number, Boolean, Object), except for maybe Error:

throw new Error('whoops!')

It's funny how the constructors are there but you don't need to use them directly.

over 1 year ago ·

@dpashkevich True indeed! It just brings all sorts of bugs. My favorite one was in a code some friends were having problems with, it looked something like this:

var hasStarted = new Boolean(false);
if(hasStarted || isViewProfile || isViewUsers ... ) { ... }

It had a lot of other flags and OR's and never thought that the hasStarted was actually being evaluated as an object. Gotta love that silly language.

over 1 year ago ·

Mind blown with {} + [], btw.

And with [] + 10, {} + 10, and so on.

Couldn't JS specification just make sense?

over 1 year ago ·

@fuadsaud There's great progress being made, though slowly. Making drastic changes might just break the internet... or a few pieces of it.

over 1 year ago ·

@zagnaut hum, I see. But, if changing things like this would break something, I can conclude that people really rely on 'features' like this, which should be considered a crime lol

but I really hope to see a JS more consistent in the - hopefully near - future.

over 1 year ago ·

There is one very good reason for use of new Array(length):

You want to duplicate a string multiple times. Let's say

var times = 5
var a = new Array(times + 1)
var newstring = a.join(oldstring)

^ The easiest way to produce placeholder strings of given lengths for number padding (preceeding zeros) or string padding (preceeding spaces) without needing to loop.

Daniel.

over 1 year ago ·

I don't thing the syntax "[10,11,12]" is a good replacement for "new Array(10,11,12)". The former embeds the API into the language syntax, the latter spells out the API clearly. This is also the reason I don't like perl compared to JavaScript. For example, if I have a better implementation for Array class, I can simply replace the name Array with MyArray.

over 1 year ago ·

@jamesxli Well, the thing is that new Array returns an object, not an actual array. You can have Array operations, but it's not a native array type. Try new Array(1,2,3) === [1,2,3]. For me, it's more misleading the fact that when I type new Array I get an object.

We don't really replace classes in Javascript, we only extend prototypes. We can do it even on run time (not a good idea, but it's possible). For instance, let's say we want to add the Array the famous foldr function and we wrap that in a specific "class" called superArray.

function superArray() {
Array.call(this);
var self = this;
this.foldr = function(fun, acum, arr) {
    if(arr.length)
        if(arr.length === 1) return fun(arr[0], acum);
        else return fun(Array.prototype.splice.apply(arr, [0,1])[0], self.foldr(fun, acum, arr));
    else return acum;
};
}

Now, if we have already created an object with our class "Array", we can overload the internal [[proto]] property to have fun.

var b = new Array(1,2,3,4);
b.prototype; // returns []
b.__proto__ = new superArray();
b.foldr(function(x, y){ return (x+y); }, 0, b) // returns 10

This is not recommended (it's a non standard and I think it's going to be dropped on Harmony), but it's possible, because Javascript holds a reference to the object's prototype, something that it's not possible in other languages. The proper way is to overload the prototype of the class we are calling.

superArray.prototype = new Array();
superArray.prototype.constructor = superArray;

This allows us to have our extended methods (from our new class) without removing the prototype chain (which changing [[proto]] does).

over 1 year ago ·

@jjperezaguinaga are you sure that the Array constructor doesn't return Array instances?

[1,2,3] === [1,2,3] // --> false
Array.isArray([1,2,3]) // --> true
Array.isArray(new Array(1,2,3)) // --> true

Also, while I never use the constructor format to initialize my array values, it's very useful for creating medium sized arrays quickly. Suppose I need to create a 10 length array quickly. It's much faster in most engines to do:

var arr = new Array(100);
for (var i = 0; i < 100; i++) {
  arr[i] = i * i;
}

vs

var arr = [];
for (var i = 0; i < 100; i++) {
  arr.push(i * i);
}

..b.ecause while arrays can resize on the fly, that isn't without cost. It does have to reallocate memory when the size required exceeds the backing store.

over 1 year ago ·

@creationix You are right, arrays created whether with new Array or [] are instances, which should make them behave as object (being stored by reference). However, this is not the case. Javascript handles Arrays in a different way it handles complex objects (Object, Function) or primitive/native ones (Number, String, Boolean). I have yet to find a way to define how Javascript manipulates Arrays, which might explain why things like the arguments variable behaves the way it behaves.

Still, I fail to see why would I use new Array(number) for a storage purpose solution (which is the reason why arrays were created by). Quick and dirty solutions like dvdotsenko's ones with the placeholders are nice usage of a tool for specific scenarios, but that's as much as I can say about that combination.

over 1 year ago ·

@jjperezaguinaga, I promise you that Array instances are normal objects just like any other object in javascript. Semantically they work just like Object instances except they have a magic .length property and the addition of a bunch of array methods like .forEach(), .map(), .join(), etc.

Arrays are stored by reference just like any other non primitive value. They are mutable. If two variables point to the same array then when the array is changed through one reference, the other reference will see the change since it is the same object.

Regarding using the constructor form with numerical lengths, I find this very useful. There are many cases that I need to quickly create an array of a known size and fill it with data that I get through a loop. For example, to implement Array.prototype.map in ES3 browsers, I would do:

if (typeof Array.prototype.map !== "function") {
  Array.prototype.map = function (callback, thisArg) {
    // Create a new array of the same size
    var length = this.length;
    var arr = new Array(length); 
    for (var i = 0, i < length; i++) {
      arr[i] = callback.call(thisArg, this[i], i, this);
    }
    return arr;
  };
}

It would be silly and slow to create an empty array and grow it using .push(). While it's technically true that setting values outside the array length also cause it to grow, the VMs are able to often optimize the backing store to the array and speed things up when you declare up front how large it is to be.

over 1 year ago ·

@creationix My, my, I did a bad test with a new Array. You are right, they behave in the same way objects do (stored by reference not by value). Pardon my mistake good sir.

In the other hand, I stand to my words: new Array is a no no. Not only because of performance, but because of readability. Multiple constructors can confuse the heck out of developers! Since Javascript syntax allows having as many arguments as we want, we would need to write extra code in order to check which function you are calling (IDE's or Code Editors can't read the prototype of the function and guess you are calling it wrong, like in Java or C++). We already have native functions with multiple optional parameters, do we really need to have our own code with ugly splices to arguments when same behaviour is expected?. The fact that the behaviour is incredible different by just adding one extra parameter is just nonsense! "If you put one parameter with value N, I create an array of length N, while if you put two, I create an array of length 2 with those two parameters as values". Woot? I would bet my magic mouse that somewhere someone is head-butting his desk because of this.

We don't need to allocate memory in Javascript. That doesn't mean that we don't care about it in Javascript. We just do it by our own ways. If you most optimal implementation of a code relies in a language feature instead of programming strategies, whenever you move from one language (add southpark meme), "You are going to have a bad time".

For instance, take this tail recursion implementation of Array.prototype.map

Array.prototype.map = function (callback, thisArg) {
var self = this, acc, i, tail_map;

acc = [];
i = 0;

tail_map = function(c, a, r, i) {
    if(self[i]) {
        r[i] = c.call(a, self[i], i, self);
        return arguments.callee(c, a, r, ++i);
    } else {
        return r;
    }
}

return tail_map(callback, thisArg, acc, i);
};

77% faster and no need to use new Array. The code is language agnostic (other than the use of call, callee or this, but you can use a named function for that) so you don't have to worry what the heck Python/Ruby/Haskell/Erlang/Scala/Clojure/PHP/Java/Dart/Groovy/Go/Scheme/Lisp/R mean when you do new Array: They all agreed that [1,2,3] is an array/list of 3 elements.

over 1 year ago ·

@dvdotsenko you can use it without creating "a" object and call "new" by var newstring = Array(times + 1).join(oldstring)

over 1 year ago ·

I'm going to agree with both @jjperezaguinaga and @creationix -- while new Array(n) is most of the time unnecessary, it is faster than just attaching items to non-existant indexes, and certainly faster than pushing.

Also, I "fixed" and extended @jjperezaguinaga perf tests to demonstrate this more clearly, as well as nearly doubling the performance of the original tailmap (why arguments.callee rather than just tail_map recursion?).

http://jsperf.com/tail-vs-iterative/2

over 1 year ago ·

@zaus Impressive work right there. Thanks for the demonstration! On why arguments.callee vs naming the function, I think it was because when I was coding the example I was using an anonymous function. My bad!

I think I'm more open today about new Array than some time ago. I think my fear is that in this Javascript world were people are not always aware or the usage of new or Function constructors, this code can be a pitfall easily avoidable. For instance, to "clean" an array, you don't do array = [] as might be logical, but array.length = 0. Oh Javascript.

Finally, unless you are doing iterative pushing, the difference in terms of performance won't kill anybody. Bugs that confuse people due non-VM oriented code, however, can turn a Dev into a serial killer.

over 1 year ago ·

i find nothing wrong with new Array(N) and the examples you provided as "wrong" or "unexpected" are completely normal. if i want to allocate 20 spaces in a new array - that's exactly what is it for. if i push something in the array (that has already 20 indeces reserved), it gets pushed further. like you said, arrays are dynamic in javascript and that's the result of it.

an example use: we want to keep last N timestamps of something and then cycle through it if we run out of space. we comfortably define the size of the N in the array definition, without having to write conditions later, like this:

//... the definition
var last_stamps = new Array(20);


//... then somewhere later on some event ...
for(var i=0;i<last_stamps.length;i++){
    if(i<last_stamps.length-1){
        last_stamps[i]=last_stamps[i+1];
    }
}
last_stamps[last_page_stamps.length-1]=(new Date()).getTime();

like first-in last-out queue

over 1 year ago ·

I would argue exactly almost the opposite as what this article suggests.

In all programming languages I can think of, [...] denotes creating an array of a certain size. So if you don't think about the content of this article too much, you might think "Oh OK, I'll never use 'new Array()'" and write something like:
[10].join('a')
... thinking this will create a string of 10 a's.

So instead of "Never use 'new Array()'", I would suggest "ALWAYS use 'new Array()' if you want to create an array with a specific size".

over 1 year ago ·

Thanks for sharing this useful code!

over 1 year ago ·