简体   繁体   English

为什么 Array.prototype.map 会忽略稀疏数组中的空槽,而 Array.prototype.join 不会?

[英]Why does Array.prototype.map ignore empty slots in a sparse array whereas Array.prototype.join does not?

The Array.prototype.map function works as expected when applied on an array with undefined values: Array.prototype.map函数在应用于具有undefined值的数组时按预期工作:

 const array = [undefined, undefined, undefined]; console.log(array.map(x => 'x')); // prints ["x", "x", "x"]

However, when using map on a sparse array with empty slots, it does not map them to 'x' as in the previous example.但是,在具有空槽的稀疏数组上使用map时,它不会像前面的示例那样将它们映射到 'x'。 Instead, it returns undefined values:相反,它返回undefined值:

 const array = [,,,]; console.log(array.map(x => 'x')); // prints [undefined, undefined, undefined]

Even if we have an array with a mix of empty slots and actual values, only the latter ones are mapped:即使我们有一个混合了空槽和实际值的数组,也只有后者被映射:

 const array = [,'a',,'b',]; console.log(array.map(x => 'x')); // prints [undefined, "x", undefined, "x"]

In contrast, I noticed Array.prototype.join works on empty slots:相比之下,我注意到Array.prototype.join适用于空插槽:

 const array = [,,,,]; console.log(array.join('x')); // prints "xxx"

Why does join treat empty slots as valid elements, but map does not?为什么join将空槽视为有效元素,而map则不然?

Furthermore, in the join documentation , they mention that if an element is undefined , null or an empty array [] , it is converted to an empty string.此外,在join 文档中,他们提到如果元素是undefinednullempty array [] ,则将其转换为空字符串。 They do not mention empty slots, but it seems they are also converting them to an empty string.他们没有提到空插槽,但似乎他们也在将它们转换为空字符串。

Is it then a problem in the MDN documentation?那么它是 MDN 文档中的问题吗? And why not having join also ignore empty slots in the same way map does?为什么不让join也像map一样忽略空槽呢? It seems to be either a problem in the documentation or in the implementation of join .这似乎是文档中的问题或join的实现中的问题。

join attempts to produce a serialized representation of the array. join尝试生成数组的序列化表示。 map produces a projection of the elements of an array through some transforming function. map通过一些转换函数生成数组元素的投影。

With map , it is possible to say: "As you step through the array, if you encounter an index that has no property, leave that property similarly unset in the output array."使用map ,可以说:“当您逐步遍历数组时,如果遇到没有属性的索引,请在输出数组中类似地保留该属性未设置。” For all existing properties, output indices will still correspond to their input indices, and the missing properties are skipped in both the input and output.对于所有现有属性,输出索引仍将对应于它们的输入索引,并且在输入和输出中都会跳过缺失的属性。

With join 's string output, we can't really do this.使用join的字符串输出,我们无法真正做到这一点。 If we join [,'a',,'b',] , an output of ,a,,b, is the best way to represent this.如果我们加入[,'a',,'b',] ,输出,a,,b,是表示这一点的最佳方式。 An output that skips missing properties -- ie, a,b -- would be hugely misleading, appearing to be a length-2 array with elements at indices 0 and 1 .跳过缺失属性(即a,b的输出会产生极大的误导,看起来是一个长度为 2 的数组,其元素在索引01

Unlike map , which can produce an array with variously present or absent properties, join is stuck rendering a string output, which cannot readily distinguish missing vs. empty properties in its output without hugely misleading results.与可以生成具有各种存在或不存在属性的数组的map不同, join被卡在呈现字符串输出中,它无法轻易区分其输出中的缺失属性和空属性,而不会产生极大的误导性结果。

For completeness, here are the actual ECMAScript-specified behaviors where the function loops through the input array (in each, k is the loop variable):为完整起见,以下是 ECMAScript 指定的实际行为,其中函数循环遍历输入数组(在每个数组中, k是循环变量):

Array.prototype.join Array.prototype.join

Repeat, while k < len重复,而k < len

  • If k > 0, set R to the string-concatenation of R and sep .如果k > 0,则将R设置为Rsep的字符串连接。
  • Let element be ?元素是? Get( O , ! ToString( k )). Get( O , !ToString( k ))。
  • If element is undefined or null , let next be the empty String;如果elementundefinednull ,让next为空字符串; otherwise, let next be ?否则,让下一步是? ToString( element ). ToString(元素)。
  • Set R to the string-concatenation of R and next .R设置为Rnext的字符串连接。
  • Increase k by 1.k增加 1。

Array.prototype.map 数组.prototype.map

Repeat, while k < len重复,而k < len

  • Let Pk be !Pk吧! ToString( k ). ToString( k )。
  • Let kPresent be ?kPresent是? HasProperty( O , Pk ). HasProperty( O , Pk )。
  • If kPresent is true , then如果kPresenttrue ,则
    • Let kValue be ?kValue为 ? Get( O , Pk ).获取( OPk )。
    • Let mappedValue be ?mappedValue是? Call( callbackfn , T , « kValue , k , O »).调用( callbackfn , T , « kValue , k , O »)。
    • Perform ?履行 ? CreateDataPropertyOrThrow( A , Pk , mappedValue ). CreateDataPropertyOrThrow( A , Pk , mappingValue )。
  • Increase k by 1.k增加 1。

Even if you don't know how to read all of this, it's plain to see that map includes a HasProperty check in the second loop step.即使您不知道如何阅读所有这些内容,也很容易看到该map在第二个循环步骤中包含HasProperty检查。 join explicitly says "If element is undefined or null , let next be the empty String." join明确表示“如果 element 是undefinednull ,让下一个是空字符串。” Get(O, ! ToString(k)) is a usual property lookup which, for ordinary objects, yields undefined when a property is absent, so the "If element is undefined " case applies. Get(O, ! ToString(k))是一种常见的属性查找,对于普通对象,当属性不存在时,它会产生undefined ,因此“如果元素undefined ”的情况适用。

It's worth noting that the MDN documentation simplifies its information in order to focus on the most common cases instead of adhering to rigorous completeness.值得注意的是,MDN 文档简化了其信息,以专注于最常见的情况,而不是坚持严格的完整性。 (I would say that sparse arrays are an uncommon case.) In particular, they say that an empty array will serialize to the empty string, which is true. (我会说稀疏数组是一种不常见的情况。)特别是,他们说空数组将序列化为空字符串,这是真的。 This is true in general for any value that has a toString function which returns an empty string:对于任何具有返回空字符串的toString函数的值,这通常是正确的:

["foo", { toString: a=>""}, "bar"].join()

This will produce the output foo,,bar .这将产生输出foo,,bar

If you're here NOT for excerpts from tutorials, but for practical ways to get array methods behave in desired way, consider the following:如果您在这里不是为了教程的摘录,而是为了让数组方法以所需方式运行的实用方法,请考虑以下事项:


 const array = [,,,]; console.log([...array].map(x => 'x'));

...if you need resulting array of initial size, or ...如果您需要初始大小的结果数组,或

 const array = [,'a',,'b',] console.log([...array].filter(Boolean).map(x => x+'x'));

...if you need to skip empty slots ...如果您需要跳过空槽

This is how JS handles sparse arrays.这就是 JS 处理稀疏数组的方式。 Consider:考虑:

> let a = [,,,,,]
> a
[ <5 empty items> ]
> let b = [undefined,undefined,undefined,undefined,undefined]
> b
[ undefined, undefined, undefined, undefined, undefined ]
> let c = [,,'x',,]
> c
[ <2 empty items>, 'x', <1 empty item> ]

Empty items do not actually take up any space (beyond some overhead).空项目实际上不占用任何空间(超出一些开销)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM