JavaScript“new Array(n)”和“Array.prototype.map”怪异

时间:2011-03-31 14:37:33

标签: javascript arrays map-function

我在Firefox-3.5.7 / Firebug-1.5.3和Firefox-3.6.16 / Firebug-1.6.2

中观察到了这一点

当我点燃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]

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

15 个答案:

答案 0 :(得分:107)

看来是第一个例子

x = new Array(3);

使用未定义的指针创建数组。

第二个创建一个数组,其中包含指向3个未定义对象的指针,在这种情况下,指向self的指针并非未定义,只有它们指向的对象。

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

由于map在数组中的对象的上下文中运行,我相信第一个映射完全无法运行该函数,而第二个映射无法运行。

答案 1 :(得分:86)

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

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

快速创建如下数组:

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

但它没有奏效,因为: 看Jonathan Lonowski回答了几个问题。

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

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

答案 2 :(得分:70)

使用ES6,您可以快速轻松地执行[...Array(10)].map((a, b) => a)

答案 3 :(得分:19)

ES6解决方案:

[...Array(10)]

不适用于打字稿(2.3),但

答案 4 :(得分:17)

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

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

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

答案 5 :(得分:14)

来自map的MDC页面:

  仅对已赋值的数组的索引调用

[...] callback; [...]

[undefined]实际上在索引上应用了setter,以便map将迭代,而new Array(1)只是使用默认值{{1}初始化索引。 } undefined跳过它。

我相信所有iteration methods都是一样的。

答案 6 :(得分:7)

我认为解释此问题的最佳方法是查看Chrome处理它的方式。

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

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

Firefox即使没有值,也会'填充'那些空undefined的空位。

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

答案 7 :(得分:6)

在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步。

方法mapeverysomeforEachslicereducereduceRight,{{1} Array将通过filter内部方法检查索引属性,因此HasProperty不会调用回调。

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

如何解决?

new Array(3).map(v => 1)

答案 8 :(得分:4)

刚碰到这个。能够使用Array(n).map确实很方便。

Array(3)收益率约为{length: 3}

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

map()实现仅对定义的属性起作用。

答案 9 :(得分:3)

不是错误。这就是定义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的所有元素的值赋值。因此,.map()函数在使用Array构造函数和数字参数新构造的数组上调用时,不会执行任何操作(不会调用回调函数)。

答案 10 :(得分:3)

如果您这样做是为了轻松填充数组中的数组,出于浏览器支持原因无法使用fill并且真的不想进行for循环,那么您也可以做x = new Array(3).join(".").split(".").map(...,它会给你一个空字符串数组。

我不得不说,相当难看,但至少问题和意图是非常明确的沟通。

答案 11 :(得分:2)

由于其他答案中已详细说明的原因,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

答案 12 :(得分: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]

答案 13 :(得分:0)

因为问题是为什么,所以这与JS的设计有关。

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

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

    < / li>
  • 简单地“未定义”:给a = [undefined, undefined]b = new Array(2)a[1]b[1]都将返回undefined,但是a[8]并且b[8]也会返回undefined,即使它们超出范围。

最终,符号empty x 3是避免设置和显示undefined的{​​{1}}值的长列表的快捷方式,因为它们没有明确声明。

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

答案 14 :(得分:-3)

在Chrome中,如果我new Array(3)我得[],那么我的猜测就是您遇到了浏览器错误。