Crockford的The Good Parts一书告诉我,JavaScript数组确实是对象。
鉴于此,为什么这不起作用? (我期待它到底是不是很奇怪?)
arr={0:'a', 1:'b', 2:'c', 3:'d'}
Array.prototype.forEach.call(arr, function(el){console.log(el)})
我的想法:如果数组是对象,那么可能像forEach
这样的方法按升序键顺序循环遍历数组的属性。 (并且通过call
,我们可以设置该方法操作的this
。)我假设这就是[].forEach.call(NodeList)
的原因。
答案 0 :(得分:3)
问题是您的对象没有length
告诉.forEach()
迭代的距离。
这很重要,因为JS数组可能是稀疏的,所以它不能简单地检查属性是否存在以确定它是否应该退出,并且它会继续递增,假设下一个索引可能有一个值。
因此,如果没有正确设置长度,迭代将失败(或之前,我不记得)第一次比较。
在您的示例中,要将对象正确地表示为类似于数组的对象,它可能如下所示:
var arr={0:'a', 1:'b', 2:'c', 3:'d', length: 4}
现在.forEach()
将成功迭代索引0-3。
答案 1 :(得分:1)
你的推理是错误的。数组是对象,但这并不意味着数组方法应该适用于任意对象。但是,他们这样做,因为spec这样说:
forEach
函数是故意通用的;它不需要 它的 this 值是一个Array对象。因此它可以 转移到其他类型的对象用作方法。
当然,为了按预期工作,对象必须足够像数组一样。这意味着它必须具有length
属性,其值是一个大于要迭代的数组索引的整数。
Array.prototype.forEach.call(
{0:'a', 1:'b', 2:'c', 3:'d', length: 4},
function(el){ console.log(el); }
);

如果您事先不知道合适的长度,可以尝试
console.log(Object.assign([], {0:'a', 1:'b', 2:'c', 3:'d'}).length); // 4

请注意,它将调用getter并跳过不可枚举的属性。构建数组后,您可以直接在其上调用forEach
而不是原始对象。
Alernatively,如果你的对象是普通的,那么getOwnPropertyNames
需要返回带有首先排序的整数索引的键。假设所有键都是整数索引,
var props = Object.getOwnPropertyNames({0:'a', 1:'b', 2:'c', 3:'d'});
console.log(props.length ? +props[props.length-1]+1 : 0); // 4

如果数组整数索引不稀疏,也可以使用props.length
。