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

ram*_*ion 192 javascript arrays map-function

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

当我开火萤火虫时:

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]
Run Code Online (Sandbox Code Playgroud)

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

Dav*_*son 117

看来是第一个例子

x = new Array(3);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • 来自[MDC](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map)(强调我的):"`map`为数组中的每个元素调用一次提供的回调函数,从结果中构造一个新数组.**`callback`仅对已赋值为**的数组的索引进行调用;对于已删除的索引或从未赋值的索引,不调用它.在这种情况下,`x`的值没有明确赋值,而`y`被赋值,即使它是值'undefined`. (83认同)
  • @TrevNorris,您可以使用`hasOwnProperty`轻松测试它,除非`hasOwnProperty`本身有一个错误:`(new Array(1)).hasOwnProperty(0)=== false`和`[undefined] .hasOwnProperty(0)= == true`.实际上,您可以使用`in`完全相同:[undefined]中的`0 === true`和新数组中的0(= 0)=== false`. (4认同)
  • 在JavaScript中谈论"未定义的指针"会混淆这个问题.您正在寻找的术语是["elisions"](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array-initializer).`x = new Array(3);`相当于`x = [,,,];`,而不是`x = [undefined,undefined,undefined]`. (3认同)
  • 那么它是一个JavaScript失败,它是不可能检查它是一个未定义的指针,还是一个未定义的指针?我的意思是`(new Array(1))[0] === [undefined] [0]`. (2认同)

cst*_*sik 103

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

let arr = new Array(10).map((val,idx) => idx);
Run Code Online (Sandbox Code Playgroud)

要快速创建这样的数组:

[0,1,2,3,4,5,6,7,8,9]
Run Code Online (Sandbox Code Playgroud)

但它没有奏效,因为:看到Jonathan Lonowski的答案,上面有几个答案.

解决方案可能是使用Array.prototype.fill()用任何值填充数组项(即使是未定义的)

let arr = new Array(10).fill(undefined).map((val,idx) => idx);
Run Code Online (Sandbox Code Playgroud)

console.log(new Array(10).fill(undefined).map((val, idx) => idx));
Run Code Online (Sandbox Code Playgroud)

更新

另一个解决方案是:

let arr = Array.apply(null, Array(10)).map((val, idx) => idx);
Run Code Online (Sandbox Code Playgroud)

console.log(Array.apply(null, Array(10)).map((val, idx) => idx));
Run Code Online (Sandbox Code Playgroud)

  • 值得注意的是,您不需要在`.fill()`方法中声明`undefined`,将代码略微简化为`let arr = new Array(10).fill().map((val,idx)= > idx);` (26认同)

小智 77

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

  • 在ES6之前你可以使用`new Array(10).fill()`.与`[... Array(10)]`的结果相同 (7认同)
  • 我知道它是如何工作的,但请为那些不知道的人添加**一些解释** (4认同)
  • 对于大型数组,扩展语法会产生问题,因此最好避免 (2认同)

Ser*_*ern 22

ES6解决方案:

[...Array(10)]
Run Code Online (Sandbox Code Playgroud)

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

  • `Array(10).fill("").map(...`是使用Typescript 2.9对我有用的东西 (5认同)

Tim*_*own 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
Run Code Online (Sandbox Code Playgroud)

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


Jon*_*ski 14

从MDC页面map:

callback仅为已赋值的数组的索引调用[...] ; [...]

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

我相信所有迭代方法都是一样的.


hel*_*dre 7

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

>>> x = new Array(3)
[]
>>> x.length
3
Run Code Online (Sandbox Code Playgroud)

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

undefined即使它没有值,Firefox也只是'填充'那些空插槽.

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


wen*_*hin 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.

方法map,every,some,forEach,slice,reduce,reduceRight,filter阵列将被检查索引属性HasProperty内部的方法,所以new 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);
Run Code Online (Sandbox Code Playgroud)


Ede*_*dau 6

由于其他答案中详细解释的原因,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
Run Code Online (Sandbox Code Playgroud)


Poi*_*nty 5

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

来自 MDC:

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

var billingMethod = new Array(5);
Run Code Online (Sandbox Code Playgroud)

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

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

var x = { }, y = { z: undefined };
if (x.z === y.z) // true
Run Code Online (Sandbox Code Playgroud)

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

  • 这就是为什么你应该使用 `x = []` 而不是 `x = new Array()` (3认同)