可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I\'ve observed this in Firefox-3.5.7/Firebug-1.5.3 and Firefox-3.6.16/Firebug-1.6.2
When I fire up Firebug:
>>> x = new Array(3)
[undefined, undefined, undefined]
>>> y = [undefined, undefined, undefined]
[undefined, undefined, undefined]
>>> x.constructor == y.constructor
true
>>> x.map(function(){ return 0; })
[undefined, undefined, undefined]
>>> y.map(function(){ return 0; })
[0, 0, 0]
What\'s going on here? Is this a bug, or am I misunderstanding how to use new Array(3)
?
回答1:
It appears that the first example
x = new Array(3);
Creates an array with undefined pointers.
And the second creates an array with pointers to 3 undefined objects, in this case the pointers them self are NOT undefined, only the objects they point to.
y = [undefined, undefined, undefined]
// The following is not equivalent to the above, it\'s the same as new Array(3)
y = [,,,];
As map is run in the context of the objects in the array I believe the first map fails to run the function at all while the second manages to run.
回答2:
I had a task that I only knew the length of the array and needed to transform the items.
I wanted to do something like this:
let arr = new Array(10).map((val,idx) => idx);
To quickly create an array like this:
[0,1,2,3,4,5,6,7,8,9]
But it didn\'t worked because:
see Jonathan Lonowski\'s answer a few answers upper.
Solution could be to fill up the array items with any value (even with undefined) using Array.prototype.fill()
let arr = new Array(10).fill(undefined).map((val,idx) => idx);
console.log(new Array(10).fill(undefined).map((val, idx) => idx));
Update
Another solution could be:
let arr = Array.apply(null, Array(10)).map((val, idx) => idx);
console.log(Array.apply(null, Array(10)).map((val, idx) => idx));
回答3:
With ES6, you can do [...Array(10)].map((a, b) => a)
, quick and easy!
回答4:
ES6 solution:
[...Array(10)]
Doesn\'t work on typescript (2.3), though
回答5:
The arrays are different. The difference is that new Array(3)
creates an array with a length of three but no properties, while [undefined, undefined, undefined]
creates an array with a length of three and three properties called \"0\", \"1\" and \"2\", each with a value of undefined
. You can see the difference using the in
operator:
\"0\" in new Array(3); // false
\"0\" in [undefined, undefined, undefined]; // true
This stems from the slightly confusing fact that if you try to get the value of a non-existent property of any native object in JavaScript, it returns undefined
(rather than throwing an error, as happens when you try to refer to a non-existent variable), which is the same as what you get if the property has previously been explictly set to undefined
.
回答6:
From the MDC page for map
:
[...] callback
is invoked only for indexes of the array which have assigned value; [...]
[undefined]
actually applies the setter on the index(es) so that map
will iterate, whereas new Array(1)
just initializes the index(es) with a default value of undefined
so map
skips it.
I believe this is the same for all iteration methods.
回答7:
I think the best way to explain this is to look at the way that Chrome handles it.
>>> x = new Array(3)
[]
>>> x.length
3
So what is actually happening is that new Array() is returning an empty array that has a length of 3, but no values. Therefore, when you run x.map
on a technically empty array, there is nothing to be set.
Firefox just \'fills in\' those empty slots with undefined
even though it has no values.
I don\'t think this is explicitly a bug, just a poor way of representing what is going on. I suppose Chrome\'s is \"more correct\" because it shows that there isn\'t actually anything in the array.
回答8:
In ECMAScript 6th edition specification.
new Array(3)
only define property length
and do not define index properties like {length: 3}
. see https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-len Step 9.
[undefined, undefined, undefined]
will define index properties and length property like {0: undefined, 1: undefined, 2: undefined, length: 3}
. see https://www.ecma-international.org/ecma-262/6.0/index.html#sec-runtime-semantics-arrayaccumulation ElementList
Step 5.
methods map
, every
, some
, forEach
, slice
, reduce
, reduceRight
, filter
of Array will check the index property by HasProperty
internal method, so new Array(3).map(v => 1)
will not invoke the callback.
for more detail, see https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array.prototype.map
How to fix?
let a = new Array(3);
a.join(\'.\').split(\'.\').map(v => 1);
let a = new Array(3);
a.fill(1);
let a = new Array(3);
a.fill(undefined).map(v => 1);
let a = new Array(3);
[...a].map(v => 1);
回答9:
Just ran into this. It sure would be convenient to be able to use Array(n).map
.
Array(3)
yields roughly {length: 3}
[undefined, undefined, undefined]
creates the numbered properties:
{0: undefined, 1: undefined, 2: undefined, length: 3}
.
The map() implementation only acts on defined properties.
回答10:
Not a bug. That\'s how the Array constructor is defined to work.
From MDC:
When you specify a single numeric parameter with the Array constructor, you specify the initial length of the array. The following code creates an array of five elements:
var billingMethod = new Array(5);
The behavior of the Array constructor depends on whether the single parameter is a number.
The .map()
method only includes in the iteration elements of the array that have explicitly had values assigned. Even an explicit assignment of undefined
will cause a value to be considered eligible for inclusion in the iteration. That seems odd, but it\'s essentially the difference between an explicit undefined
property on an object and a missing property:
var x = { }, y = { z: undefined };
if (x.z === y.z) // true
The object x
does not have a property called \"z\", and the object y
does. However, in both cases it appears that the \"value\" of the property is undefined
. In an array, the situation is similar: the value of length
does implicitly perform a value assignment to all the elements from zero through length - 1
. The .map()
function therefore won\'t do anything (won\'t call the callback) when called on an array newly constructed with the Array constructor and a numeric argument.
回答11:
If you are doing this in order to easily fill up an array with values, can\'t use fill for browser support reasons and really don\'t want to do a for-loop, you can also do x = new Array(3).join(\".\").split(\".\").map(...
which will give you an array of empty strings.
Quite ugly I have to say, but at least the problem and intention are quite clearly communicated.
回答12:
Here\'s a simple utility method as a workaround:
Simple mapFor
function mapFor(toExclusive, callback) {
callback = callback || function(){};
var arr = [];
for (var i = 0; i < toExclusive; i++) {
arr.push(callback(i));
}
return arr;
};
var arr = mapFor(3, function(i){ return i; });
console.log(arr); // [0, 1, 2]
arr = mapFor(3);
console.log(arr); // [undefined, undefined, undefined]
Complete Example
Here\'s a more complete example (with sanity checks) which also allows specifying an optional starting index:
function mapFor() {
var from, toExclusive, callback;
if (arguments.length == 3) {
from = arguments[0];
toExclusive = arguments[1];
callback = arguments[2];
} else if (arguments.length == 2) {
if (typeof arguments[1] === \'function\') {
from = 0;
toExclusive = arguments[0];
callback = arguments[1];
} else {
from = arguments[0];
toExclusive = arguments[1];
}
} else if (arguments.length == 1) {
from = 0;
toExclusive = arguments[0];
}
callback = callback || function () {};
var arr = [];
for (; from < toExclusive; from++) {
arr.push(callback(from));
}
return arr;
}
var arr = mapFor(1, 3, function (i) { return i; });
console.log(arr); // [1, 2]
arr = mapFor(1, 3);
console.log(arr); // [undefined, undefined]
arr = mapFor(3);
console.log(arr); // [undefined, undefined, undefined]
Counting Down
Manipulating the index passed to the callback allows counting backwards:
var count = 3;
var arr = arrayUtil.mapFor(count, function (i) {
return count - 1 - i;
});
// arr = [2, 1, 0]
回答13:
In Chrome, if I do new Array(3)
I get []
, so my guess is that you\'ve come across a browser bug.