在不是数组的可迭代对象上调用 Array.map 是否有效?

Is it valid to call Array.map on an iterable that is not an array?

这篇文章:Traversing the DOM with filter(), map(), and arrow functions 提倡对 Array.map 的这种使用,这对我来说似乎很奇怪。更具体地说,文章的作者声称以下代码是有效的,并且实际上比替代方案 Array.from(elements).map(...):

更好
var elements = document.getElementsByClassName("bgflag");
BgFlags = Array.prototype.map.call(elements,
        element =>
        ({
            height: element.offsetTop,
            bgsrc: element.dataset.bgsrc,
            bgcolor: element.dataset.bgcolor,
            size: element.dataset.size,
            name: element.id,
            image: parseInt(element.dataset.image)
        })
    );

对于我未经训练的人来说,这似乎是最可疑的。我们正在调用 Array.prototype.map 不是 Array 的东西。除非在某个地方明确说明这是允许的,否则这对我来说就像是未定义的行为。我快速浏览了 the relevant MDN documentation,但找不到那里允许这样的用法。

然而,文章的作者强调:

Even though map() is a function of Array.prototype, it works on any array-like iterable.

这样的使用是否像他声称的那样有效?如果是这样,其他 Array.prototype.* 函数是否也是这种情况,例如 filterslice,甚至 poppush、其他函数?

是的,通常有效。但是我同意你的观点:

Array.from(elements).map(element => /*..*/);
// Or even
Array.from(elements, element => /*...*/);

干净多了。

Unless it is somewhere explicitely stated that this is allowed

是的,它在the specification:

The map function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method.

Array.prototype 上的大多数方法都有该说明。不能那样用的就没有那个备注。

因此,是否要使用它纯粹是风格问题。不同的人会对它是否 good/bad/indifferent 风格有不同的看法。


旁注:如果你打算那样使用 map,而不是每次都手写,给自己一个快捷方式:

const map = Function.prototype.call.bind(Array.prototype.map);

然后像这样使用它:

const result = map(arrayLike, e => /*...*/);

实例:

const map = Function.prototype.call.bind(Array.prototype.map);

const arrayLike = {
  0: "zero",
  1: "one",
  2: "two",
  length: 3
};
const result = map(arrayLike, e => e.toUpperCase());
console.log(result);