Undefined Values in Array(Len) Initializer

Undefined values in Array(len) initializer

The array constructor creates an array with the given length. It does not create the keys. Array.prototype.map's callback function is only executed for the elements in the list.

That is, all values which are associated with a key (integer) 0 ≤ i < length.

  • Array(3) has zero keys, so .map's callback is never triggered.
  • [void 0, void 0, void 0] has three keys, for which the callback function is executed.

    Array(3).hasOwnProperty(0);                 // false
    [void 0, void 0, void 0].hasOwnProperty(0); // true

The specification and its polyfill are mentioned at MDN. At line 47, if (k in O) { shows that non-existant keys are not treated by the callback function.

How [undefined, undefined, undefined] and new Array(3) are different in JavaScript?

The second two are the same (though the second one might cause issues on some browsers), but the first is different.

The first one

var arr1 = [undefined, undefined, undefined];

makes explicit assignments to the first three indexes in the array. Even though you're assigning undefined, the fact that indexes 0, 1, and 2 are targets of assignment operations means that those elements of the array will be treated as "real" by methods like .reduce():

[undefined, undefined, undefined].reduce(function(c) { return c + 1; }, 0);
// 3

[,,].reduce(function(c) { return c + 1; }, 0);
// 0

Many of the Array.prototype methods skip uninitialized array elements.

Javascript - Initialize array with nulls

With EcmaScript 6 (ES2105), creating an array containing five nulls is as easy as this:

const arr = new Array(5).fill(null);

MDN Reference

How to initialize an array's length in JavaScript?

  1. Why do you want to initialize the length? Theoretically there is no need for this. It can even result in confusing behavior, because all tests that use the length to find out whether an array is empty or not will report that the array is not empty.

    Some tests show that setting the initial length of large arrays can be more efficient if the array is filled afterwards, but the performance gain (if any) seem to differ from browser to browser.

  2. jsLint does not like new Array() because the constructer is ambiguous.

    new Array(4);

    creates an empty array of length 4. But

    new Array('4');

    creates an array containing the value '4'.

Regarding your comment: In JS you don't need to initialize the length of the array. It grows dynamically. You can just store the length in some variable, e.g.

var data = [];
var length = 5; // user defined length

for(var i = 0; i < length; i++) {
data.push(createSomeObject());
}

Array constructor not working as expected

From the MDN: this returns a new JavaScript array with length set to that number

The Array created by new Array(n) does not have any elements, only a length. That's why you can't map the (not existing) elements.

Undefined values with new Array() in JavaScript

So from what I could tell, it's calling Array() as if it was passed 10 arguments, but since it doesn't define any of those "integer properties", it's as if it was passed 10 undefined arguments. Is that correct?

Yes, exactly. It's doing this:

var arr = Array(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
var barr = new Array(10);
console.log(barr);

...

console.log(arr.map(function(item) { return 'hi';}));
console.log(barr.map(function(item) { return 'hi';}));

I wanted to know why the map function didn't work for the second one.

Because map, forEach, and similar only visit properties that actually exist. As you said, there's a big difference between.

var arr = Array(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
// Or `var arr = Array.apply(null, {length: 10});

and

var barr = new Array(10);

In the first example, arr has 10 entries, each of which has the value undefined. In the second example, barr has no entries and a length of 10. So in the first one, map will visit the properties, because they exist. In the second one, it won't, because they don't.

Recall that standard arrays in JavaScript aren't really arrays at all (disclosure: that's a post on my blog), they're objects with some special behavior. As such, they're inherently sparse:

var a = [];
a.length = 10000;

a doesn't have 10,000 entries. It has no entries. It's just that its length property is 10000.

You can tell whether the property exists by using hasOwnProperty or in. Compare:

var barr = new Array(10);
console.log(barr.hasOwnProperty(0)); // false
console.log(0 in barr); // false

to:

var arr = Array.apply(null, {length: 10});
console.log(arr.hasOwnProperty(0)); // true
console.log(0 in arr); // true

And Array.apply works because it asks {length: 10} what its property 0 is, receives undefined, and so assigns undefined to the property 0 of the array it's constructing, and so on. Is my reasoning correct?

Yes, although to be clear, it's apply that's asking what property 0 is, and then it's using that as the first argument when calling Array. It's Array that then takes the first argument's value and assigns it to the 0 property on the array it's creating.

And is there really no less hackish way to define an array which contains undefined integer properties for each index within its range, rather than having no integer properties at all?

Only slightly: ES2015 adds Array.from, which accepts an array-like object and returns a true array (optionally mapping the entries). So that would be:

var arr = Array.from({length:10});

It's rare to need to do that, as opposed to simply a = new Array(bigNumberHere); or a = []; a.length = bigNumberHere. E.g., many times you don't care if the property doesn't exist or exists with the value undefined. Sometimes you do, but to give you perspective, I've been writing JavaScript professionally for 20 years, fairly intensively the last 8, and I've probably cared, oh, once or twice, tops.

You mentioned that in the specific case you were dealing with, it was combined with map, so Array.from would take the place of both:

var arr = Array.from({length: 10}, function() { return "hi"; });

...yields

["hi", "hi", "hi", "hi", "hi", "hi", "hi", "hi", "hi", "hi"]

Although Array.from is new in ES2015, it can be shimmed/polyfilled on older JavaScript engines.

Javascript array contains only undefined after initialization, not the given values

Don't initialize arrays like that. Always do this instead:

var myarray = [value, value, value, ... ];

The "Array()" constructor is terribly designed. The single-argument form, when the argument is a number, is interpreted as a request to "initialize" an array with that many "empty" values. It's a pointless thing to do, so in general you're much better off using the array constant notation (as in my example above).

It doesn't seem to happen anymore in modern browsers, but I'd swear that there was a time that at least some browsers would actually allocate memory for the single-argument constructor, which was not really useful but dangerous to code that might accidentally pass in a single very large number.

Is a new JavaScript array with length unusable?

new Array(10) doesn't return an array filled with 10 undefineds. Instead, it returns an array with no elements, and with a length property of 10.

See the difference:

var arr1 = new Array(1),
arr2 = [undefined];
arr1.length === arr2.length; // Both are `1`
arr1[0] === arr2[1]; // Both are `undefined`
arr1.hasOwnProperty(0); // false
arr2.hasOwnProperty(0); // true

Therefore, ECMAScript 5 array methods skip those non-existing properties. Specifically, when they iterate from 0 to length, they check the [[HasProperty]] internal method of the array.

You can fix it easily with ECMAScript 6 Array.prototype.fill (which can be polyfilled):

new Array(10).fill().map(Math.random);


Related Topics



Leave a reply



Submit