Looking at some javascript code, I saw (something like) this:
var arr = Array.apply(null, {length: 10});
Reading the MDN documentation for Function.prototype.apply(), I learnt that although it usually expects an array as its second argument which is an array of the arguments to be passed to the invoked function,
you can also use any kind of object which is array-like, so in practice this means it's going to have a property length and integer properties in the range (0...length).
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?
console.log(arr);
yields
[ undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined ]
Which is totally different to the result of
var barr = new Array(10);
console.log(barr);
which is
[ , , , , , , , , , ]
These arrays also behave differently.
console.log(arr.map(function(item) { return 'hi';}));
console.log(barr.map(function(item) { return 'hi';}));
gives
[ 'hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi' ]
[ , , , , , , , , , ]
I wanted to know why the map function didn't work for the second one. So I checked console.log(1 in arr);
which gave true and `console.log(1 in barr);' which gave false.
So my current guess is that arr is an array which contains as integer properties 10 variables which each have the value of undefined and barr is an array which, although it has a length of 10, has no integer properties. 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? 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? Because looking at this, it seems to me that new Array()
is pretty useless.
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 property0
is, receivesundefined
, and so assignsundefined
to the property0
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.
Is that correct?
Yes
you can also use any kind of object which is array-like, so in practice this means it's going to have a property length and integer properties in the range (0...length).
It says that you can pass a duck-type of an array: an object that has a sort of interface:
This will be treated as an Array. So passing a { length: 10 } is like pass a real array [undefined,undefined,undefined,..] where each index is created and has value undefined.
So the first code row:
var arr = Array.apply(null, {length: 10});
is interpreted as:
var arr = Array(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
So my current guess is that arr is an array which contains as integer properties 10 variables which each have the value of undefined and barr is an array which, although it has a length of 10, has no integer properties.
Yes.
var test = Array(10);
it outputs: [undefined x10] <-- no indexes, only a simple property 'length' unaligned with the real content, but useful for retrieve it and do some size condiderations.
test[2] = undefined;
it outputs: [undefined x2, undefined, undefined x7] <-- no indexes except for the position '2' that has a value: undefined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With