forEach()vs Array.prototype.forEach.call()

时间:2017-05-02 17:06:28

标签: javascript

我刚刚通过电子API演示代码示例突然出现了一个狂野的表达 - 这对我来说是完全陌生的 - 出现了:

const links = document.querySelectorAll('a[href]');

Array.prototype.forEach.call(links, function (link) {
    // WWIII here
})

我明白这段代码正在做什么,但我习惯了这样的语法:

links.forEach(function (links) {});

那两者究竟有什么区别?我已经阅读了有关此主题的各种StackOverflow线程,但它们要么含糊不清要么根本不回答这个问题。有人说它有类似于数组的集合,不能被.forEach()迭代,而不是Array.prototype.forEach.call()。这是过度繁琐和长版本的唯一优势吗?

提前致谢!

3 个答案:

答案 0 :(得分:23)

JavaScript are actually functions defined on a prototype中的

“类方法”。这意味着即使对象不从the Array prototype继承,也可以在其上调用Array方法,只要它遵循数组结构(即:它是一个{{1}的对象。由整数索引的属性和属性)。但是,该对象不引用length,因此您需要明确选择Array.prototype作为方法所在的对象。

document.querySelectorAll函数返回NodeList,它既不是Array.prototype也不是Array原型的继承。但是,由于Array具有与NodeList类似的内部结构,您仍然可以使用Array函数。但由于forEach未继承NodeList原型,因此尝试在Array上使用.forEach会引发错误(这不是完全是的 - 请参阅我的答案末尾的注释。出于这个原因,您需要明确声明您正在NodeListArray.prototype调用方法,并且使用the .call method from Function.prototype来完成。

总结:

NodeList

表示:

Array.prototype.forEach.call(links, function(link) { /* something */ }) 获取forEach函数并在Array.prototype上调用它,这是一个非links对象,并以某个函数作为参数。

请注意,在最新版本的浏览器中,Array原型 提供a forEach method,其工作方式与NodeList相同,因此示例来自Electron API可能使用Array版本与旧版本兼容。如果您有一个网络应用程序并且只关心支持现代版Chrome和Firefox,那么您只需拨打Array上的forEach即可。实际上,从Electron updates about 2 weeks after whenever Chrome updates开始,在Electron中使用NodeList应该是安全的。 :)

答案 1 :(得分:3)

这是一个有趣的问题。半年前我会说link.forEach不是关于缩短语法,但它实际上不应该起作用。然后我会解释这意味着许多数组方法故意泛型,这意味着它们的内部实现只考虑this对象的数字索引和长度属性,但不是关心它是数组实例。基本上@Pedro Castilho在他的回答中说道。

但是,现在我要说现在常绿浏览器(除了IE11,Edge,截至2017年4月)已经实现了NodeList.prototype.forEach便捷方法,因此您不再需要使用.call hack或{{ 1}}以便用Array.from迭代NodeList。

所以我的摘要:如果您不需要支持IE,请使用forEach而不是NodeList.prototype.forEach。它在内部可能是相同的,但在概念上更清洁。如果您确实需要支持IE,并且您不想再添加一个pollyfill,请使用Array.prototype.forEach或更好Array.prototype.call

答案 2 :(得分:2)

这可能与document.querySelectorAll返回a static NodeList而不是an Array的方式有关。

使用Array的原型,您仍然可以拨打forEach,它类似于operating on arguments