268

我在 Firefox-3.5.7/Firebug-1.5.3 和 Firefox-3.6.16/Firebug-1.6.2 中观察到了这一点

当我启动 Firebug 时:

var x = new Array(3)
console.log(x) 
// [undefined, undefined, undefined]

var y = [undefined, undefined, undefined]
console.log(y) 
// [undefined, undefined, undefined]

console.log( x.constructor == y.constructor) // true

console.log( 
  x.map(function() { return 0; })
)
// [undefined, undefined, undefined]

console.log(
  y.map(function() { return 0; })
)
// [0, 0, 0]

这里发生了什么?这是一个错误,还是我误解了如何使用new Array(3)

4

14 回答 14

163

我有一个任务,我只知道数组的长度,需要转换项目。我想做这样的事情:

let arr = new Array(10).map((val,idx) => idx);

要快速创建这样的数组:

[0,1,2,3,4,5,6,7,8,9]

但这没有用,因为:(请参阅上面的几个答案乔纳森·洛诺夫斯基的回答)

解决方案可能是使用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));

更新

另一种解决方案可能是:

let arr = Array.apply(null, Array(10)).map((val, idx) => idx);

console.log(Array.apply(null, Array(10)).map((val, idx) => idx));

于 2016-01-29T13:57:59.270 回答
146

看来第一个例子

x = new Array(3);

创建一个长度为 3 但没有任何元素的数组,因此不会创建索引 [0]、[1] 和 [2]。

第二个创建一个包含 3 个未定义对象的数组,在这种情况下,它们自己创建了索引/属性,但它们引用的对象是未定义的。

y = [undefined, undefined, undefined]
// The following is not equivalent to the above, it's the same as new Array(3)
y = [,,,];

由于 map 在索引/属性列表上运行,而不是在设置的长度上运行,因此如果没有创建索引/属性,它将不会运行。

于 2011-03-31T14:45:57.347 回答
105

使用 ES6,你可以做到[...Array(10)].map((a, b) => a),又快又容易!

于 2016-10-05T16:20:16.037 回答
29

ES6 solution:

[...Array(10)]

Doesn't work on typescript (2.3), though

于 2017-05-05T06:22:51.787 回答
23

从 MDC 页面获取map

[...]callback仅对具有赋值的数组索引调用;[...]

[undefined]实际上将 setter 应用于 index(es) 以便map进行迭代,而new Array(1)只是使用默认值初始化 index(es)undefined所以map跳过它。

我相信这对于所有迭代方法都是一样的。

于 2011-03-31T14:47:10.827 回答
20

数组不同。不同之处在于new Array(3)创建一个长度为三个但没有属性[undefined, undefined, undefined]的数组,而创建一个长度为三个的数组和三个名为“0”、“1”和“2”的属性,每个属性的值为undefined。您可以使用运算符查看差异in

"0" in new Array(3); // false
"0" in [undefined, undefined, undefined]; // true

这源于一个稍微令人困惑的事实,即如果您尝试获取 JavaScript 中任何本机对象的不存在属性的值,它会返回undefined(而不是抛出错误,就像您尝试引用不存在的变量时发生的那样) ),如果该属性先前已明确设置为 ,则与您得到的相同undefined

于 2011-03-31T14:48:48.113 回答
11

由于其他答案中彻底解释的原因,Array(n).map不起作用。然而,在 ES2015 中Array.from接受一个 map 函数:

let array1 = Array.from(Array(5), (_, i) => i + 1)
console.log('array1', JSON.stringify(array1)) // 1,2,3,4,5

let array2 = Array.from({length: 5}, (_, i) => (i + 1) * 2)
console.log('array2', JSON.stringify(array2)) // 2,4,6,8,10

于 2020-03-26T09:35:44.950 回答
9

在 ECMAScript 第 6 版规范中。

new Array(3)只定义属性length,不定义索引属性,如{length: 3}. 请参阅https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-len步骤 9。

[undefined, undefined, undefined]将定义索引属性和长度属性,如{0: undefined, 1: undefined, 2: undefined, length: 3}. 请参阅https://www.ecma-international.org/ecma-262/6.0/index.html#sec-runtime-semantics-arrayaccumulation ElementList步骤 5。

Array 的methods map, every, some, forEach, slice, reduce, reduceRight,会通过内部方法检查 index 属性,所以不会调用回调。filterHasPropertynew Array(3).map(v => 1)

有关更多详细信息,请参阅https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array.prototype.map

怎么修?

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);
于 2017-04-18T08:16:18.733 回答
7

我认为解释这一点的最佳方法是查看 Chrome 处理它的方式。

>>> x = new Array(3)
[]
>>> x.length
3

所以实际发生的是 new Array() 返回一个长度为 3 但没有值的空数组。因此,当您x.map技术上为空的数组上运行时,无需设置任何内容。

Firefox 只是“填充”那些空槽,undefined即使它没有任何值。

我不认为这明确是一个错误,只是表示正在发生的事情的一种糟糕的方式。我想 Chrome 是“更正确的”,因为它表明数组中实际上没有任何东西。

于 2011-03-31T14:47:52.590 回答
5

不是错误。这就是定义 Array 构造函数的工作方式。

来自 MDC:

当您使用 Array 构造函数指定单个数值参数时,您指定了数组的初始长度。以下代码创建一个包含五个元素的数组:

var billingMethod = new Array(5);

Array 构造函数的行为取决于单个参数是否为数字。

.map()方法仅在数组的迭代元素中包含明确分配了值的元素。即使是显式赋值undefined也会导致一个值被认为有资格包含在迭代中。undefined这看起来很奇怪,但它本质上是对象上的显式属性和缺失属性之间的区别:

var x = { }, y = { z: undefined };
if (x.z === y.z) // true

该对象x没有名为“z”的属性,而该对象y有。但是,在这两种情况下,财产的“价值”似乎都是undefined。在数组中,情况类似: 的值length确实对从零到 的所有元素进行了值赋值length - 1。因此,在使用 Array 构造函数和数字参数新建的数组上调用该函数时,该.map()函数不会做任何事情(不会调用回调)。

于 2011-03-31T14:46:52.940 回答
4

刚碰到这个。能够使用肯定会很方便Array(n).map

Array(3)产量大致{length: 3}

[undefined, undefined, undefined]创建编号属性:
{0: undefined, 1: undefined, 2: undefined, length: 3}.

map() 实现只作用于定义的属性。

于 2013-10-29T02:01:55.550 回答
3

如果您这样做是为了轻松地用值填充数组,由于浏览器支持的原因不能使用填充,并且真的不想做一个 for 循环,您也可以这样做x = new Array(3).join(".").split(".").map(...会给您一个空数组字符串。

我不得不说这很丑,但至少问题和意图已经很清楚地传达了。

于 2016-03-21T14:42:49.990 回答
1

既然问题是为什么,这与 JS 的设计方式有关。

我可以想到解释这种行为的两个主要原因:

  • 性能:给定x = 10000new Array(x)构造函数避免从 0 到 10000 循环以用undefined值填充数组是明智的。

  • 隐式“未定义”:给出a = [undefined, undefined]and b = new Array(2), a[1]andb[1]都将返回undefined,但a[8]andb[8]也会返回,undefined即使它们超出范围。

最终,符号empty x 3是避免设置和显示一长串undefined值的捷径,undefined因为它们没有显式声明。

注意:给定数组a = [0]a[9] = 9console.log(a)将返回(10) [0, empty x 8, 9],通过返回显式声明的两个值之间的差异自动填补空白。

于 2019-09-15T13:37:12.700 回答
0

这是一个简单的实用程序方法作为解决方法:

简单的地图

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]

完整示例

这是一个更完整的示例(带有完整性检查),它还允许指定可选的起始索引:

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]

倒计时

操作传递给回调的索引允许向后计数:

var count = 3;
var arr = arrayUtil.mapFor(count, function (i) {
    return count - 1 - i;
});
// arr = [2, 1, 0]
于 2018-10-10T08:36:12.533 回答