在一个对象上调用forEach

时间:2017-01-08 22:54:33

标签: javascript arrays

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)的原因。

2 个答案:

答案 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