.forEach.call()在JavaScript中做什么?
我正在查看一些代码片段,并且发现多个元素通过节点列表调用一个函数,forEach应用于一个空的数组。
比如我有这样的东西:
[].forEach.call( document.querySelectorAll('a'), function(el) { // whatever with the current node });
但我不明白它是如何工作的。 任何人都可以解释我在forEach前的空数组的行为,以及如何call
?
[]
是一个数组。
这个数组根本不使用。
它被放在页面上,因为使用数组可以访问数组原型,比如.forEach
。
这比键入Array.prototype.forEach.call(...);
要快Array.prototype.forEach.call(...);
接下来, forEach
是一个函数,它将函数作为input…
[1,2,3].forEach(function (num) { console.log(num); });
…对于每个元素(在this
数组中,它有一个length
,你可以像this[1]
访问它的部分) this[1]
它会传递三个东西:
- 数组中的元素
- 元素的索引(第三个元素会传递
2
) - 对数组的引用
最后, .call
是函数的原型(这是一个被其他函数调用的函数)。
.call
将会接受它的第一个参数,并把它作为第一个参数( undefined
或者null
在日常的JS中使用window
,或者如果在“严格模式” )。 剩下的参数将被传递给原始函数。
[1, 2, 3].forEach.call(["a", "b", "c"], function (item, i, arr) { console.log(i + ": " + item); }); // 0: "a" // 1: "b" // 2: "c"
因此,您正在创build一个调用forEach
函数的快速方法,并将this
从空数组更改为所有<a>
标记的列表,并且对于每个<a>
顺序,您正在调用函数提供。
编辑
逻辑结论/清理
下面有一篇文章的链接,指出我们在函数式编程中会放弃尝试,并且每次都要坚持手动内联循环,因为这个解决scheme是黑客和难看的。
我会说,虽然.forEach
不如其对应的.map(transformer)
.filter(predicate)
.reduce(combiner, initialValue)
,但当你真正想做的只是修改外部世界(不是数组),n次,同时有权访问arr[i]
或i
。
那么,我们该如何处理这种差距,正如Motto显然是一个天才和知识渊博的人,我想我想象我知道我在做什么/我要去哪里(现在或者之后……)这是头先学习)?
答案其实很简单,由于监督,鲍勃叔叔和克罗克福爵士都会面临这样的问题:
把它清理干净
function toArray (arrLike) { // or asArray(), or array(), or *whatever* return [].slice.call(arrLike); } var checked = toArray(checkboxes).filter(isChecked); checked.forEach(listValues);
现在,如果你在质疑自己是否需要这样做,答案可能不是…
这个确切的事情是由…这些天具有高阶function的每个(?)图书馆完成的。
如果你使用lodash或者下划线,或者甚至是jQuery,他们都会有一套方法来获取一组元素,然后执行一个n次的动作。
如果你不使用这样的事情,那么一定要写你自己的。
lib.array = (arrLike, start, end) => [].slice.call(arrLike, start, end); lib.extend = function (subject) { var others = lib.array(arguments, 1); return others.reduce(appendKeys, subject); };
ES6(ES2015)及更高版本的更新
slice( )
/ array( )
/ etc的帮助程序方法不但可以让那些想要像使用数组一样使用列表的人更容易,但是对于那些在ES6 +相对不远的将来的浏览器,或者今天在巴别塔的“传译”,你都有内置的语言function,这使得这种types的东西是不必要的。
function countArgs (...allArgs) { return allArgs.length; } function logArgs (...allArgs) { return allArgs.forEach(arg => console.log(arg)); } function extend (subject, ...others) { /* return ... */ } var nodeArray = [ ...nodeList1, ...nodeList2 ];
超级干净,非常有用。
查找Rest和Spread操作符; 在BabelJS站点尝试它们; 如果你的技术堆栈是有序的,在Babel生产环境中使用它们和构build步骤。
没有什么好的理由不能使用从非数组到数组的转换……只是不要把你的代码乱成一团,无所事事, 而是把所有的同一个丑陋的行粘在一起。
querySelectorAll
方法返回一个NodeList
,类似于一个数组,但它不是一个数组。 因此,它没有forEach
方法(哪个数组对象通过Array.prototype
inheritance)。
由于NodeList
类似于数组,因此数组方法实际上可以在其上工作,所以通过使用[].forEach.call
您可以调用NodeList
上下文中的Array.prototype.forEach
方法,就好像您已经能够只需做yourNodeList.forEach(/*...*/)
。
请注意,空数组文字只是扩展版本的快捷方式,您可能也会经常看到:
Array.prototype.forEach.call(/*...*/);
其他答案很好地解释了这个代码,所以我只是添加一个build议。
这是代码的一个很好的例子,为了简单和清晰,应该重构代码。 每次执行此操作时,不要使用[].forEach.call()
或Array.prototype.forEach.call()
,而要使用一个简单的函数:
function forEach( list, callback ) { Array.prototype.forEach.call( list, callback ); }
现在你可以调用这个函数,而不是更复杂和晦涩的代码:
forEach( document.querySelectorAll('a'), function( el ) { // whatever with the current node });
这可以写得更好
Array.prototype.forEach.call( document.querySelectorAll('a'), function(el) { });
什么是document.querySelectorAll('a')
返回一个类似于数组的对象,但它不是从Array
typesinheritance的。 所以我们使用上下文作为document.querySelectorAll('a')
返回的值来调用Array.prototype
对象的forEach
方法
一个空的数组在其原型中有一个forEach
属性,它是一个Function对象。 (空数组只是获取所有Array
对象具有的forEach
函数的引用的简单方法。)函数对象又具有一个也是函数的call
属性。 当你调用函数的call
函数时,它会使用给定的参数运行函数。 第一个参数变成被调用的函数。
您可以在这里findcall
function的文档。 forEach
文档在这里 。
只需添加一行:
NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;
瞧!
document.querySelectorAll('a').forEach(function(el) { // whatever with the current node });
请享用 :-)
警告: NodeList是一个全局类。 如果你写公共图书馆,不要使用这个build议。 然而,当您在网站或node.js应用程序上工作时,提高自我效能是非常方便的方法。
[]
总是返回一个新的数组,它相当于new Array()
但保证返回一个数组,因为Array
可以被用户覆盖,而[]
不能。 所以这是获得Array
原型的一个安全的方法,然后如上所述,使用call
来执行arraylike nodelist(this)上的函数。
使用给定的值和单独提供的参数来调用函数。 MDN
Norguard解释了WHAT [].forEach.call()
和James Allardice 为什么要这么做:因为querySelectorAll返回一个没有forEach方法的NodeList
…
除非你有Chrome 51+,Firefox 50+,Opera 38,Safari 10等现代浏览器。
如果没有,你可以添加一个Polyfill :
if (window.NodeList && !NodeList.prototype.forEach) { NodeList.prototype.forEach = function (callback, thisArg) { thisArg = thisArg || window; for (var i = 0; i < this.length; i++) { callback.call(thisArg, this[i], i, this); } }; }