[].forEach.call() 在 JavaScript 中有什么作用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16053357/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What does [].forEach.call() do in JavaScript?
提问by Mimo
I was looking at some snippets of code, and I found multiple elements calling a function over a node list with a forEach applied to an empty array.
我正在查看一些代码片段,我发现多个元素在节点列表上调用函数,并将 forEach 应用于空数组。
For example I have something like:
例如我有类似的东西:
[].forEach.call( document.querySelectorAll('a'), function(el) {
// whatever with the current node
});
but I can't understand how it works. Can anyone explain me the behaviour of the empty array in front of the forEach and how the call
works?
但我无法理解它是如何工作的。谁能解释一下 forEach 前面的空数组的行为以及它是如何call
工作的?
回答by Norguard
[]
is an array.
This array isn't used at all.
[]
是一个数组。
根本不使用这个数组。
It's being put on the page, because using an array gives you access to array prototypes, like .forEach
.
它被放在页面上,因为使用数组可以让您访问数组原型,例如.forEach
.
This is just faster than typing Array.prototype.forEach.call(...);
这比打字快 Array.prototype.forEach.call(...);
Next, forEach
is a function which takes a function as an input...
接下来,forEach
是一个将函数作为输入的函数......
[1,2,3].forEach(function (num) { console.log(num); });
...and for each element in this
(where this
is array-like, in that it has a length
and you can access its parts like this[1]
) it will pass three things:
...并且对于中的每个元素this
(其中this
类似于数组,因为它有一个length
并且您可以访问它的部分,例如this[1]
),它将传递三件事:
- the element in the array
- the index of the element (third element would pass
2
) - a reference to the array
- 数组中的元素
- 元素的索引(第三个元素将通过
2
) - 对数组的引用
Lastly, .call
is a prototype which functions have (it's a function which gets called on other functions)..call
will take its first argument and replace this
inside of the regular function with whatever you passed call
, as the first argument (undefined
or null
will use window
in everyday JS, or will be whatever you passed, if in "strict-mode"). The rest of the arguments will be passed to the original function.
最后,.call
是一个函数具有的原型(它是一个被其他函数调用的函数)。.call
将采用它的第一个参数并将this
常规函数内部替换为您传递的任何内容call
,作为第一个参数(undefined
或null
将window
在日常 JS 中使用,或者将是您传递的任何内容,如果在“严格模式”中)。其余参数将传递给原始函数。
[1, 2, 3].forEach.call(["a", "b", "c"], function (item, i, arr) {
console.log(i + ": " + item);
});
// 0: "a"
// 1: "b"
// 2: "c"
Therefore, you're creating a quick way to call the forEach
function, and you're changing this
from the empty array to a list of all <a>
tags, and for each <a>
in-order, you are calling the function provided.
因此,您正在创建一种调用forEach
函数的快速方法,并且您正在将this
空数组更改为所有<a>
标签的列表,并且对于每个<a>
顺序,您正在调用提供的函数。
EDIT
编辑
Logical Conclusion / Cleanup
逻辑结论/清理
Below, there's a link to an article suggesting that we scrap attempts at functional programming, and stick to manual, inline looping, every time, because this solution is hack-ish and unsightly.
下面是一篇文章的链接,建议我们放弃函数式编程的尝试,并每次都坚持手动、内联循环,因为这个解决方案是黑客式的且难看的。
I'd say that while .forEach
is less helpful than its counterparts, .map(transformer)
, .filter(predicate)
, .reduce(combiner, initialValue)
, it still serves purposes when all you really want to do is modify the outside world (not the array), n-times, while having access to either arr[i]
or i
.
我想说的是,虽然.forEach
比其同行少乐于助人,.map(transformer)
,.filter(predicate)
,.reduce(combiner, initialValue)
,它仍然成为目的时,你真正想要做的是改变外界(不是数组),n次,虽然能获得两种arr[i]
或i
。
So how do we deal with the disparity, as Motto is clearly a talented and knowledgeable guy, and I would like to imagine that I know what I'm doing/where I'm going (now and then... ...other times it's head-first learning)?
那么我们如何处理这种差异,因为 Motto 显然是一个有才华和知识渊博的人,我想想象我知道我在做什么/我要去哪里(不时......其他有时是头部优先学习)?
The answer is actually quite simple, and something Uncle Bob and Sir Crockford would both facepalm, due to the oversight:
答案实际上很简单,由于疏忽,鲍勃叔叔和克罗克福德爵士都会面临一些问题:
clean it up.
清理它。
function toArray (arrLike) { // or asArray(), or array(), or *whatever*
return [].slice.call(arrLike);
}
var checked = toArray(checkboxes).filter(isChecked);
checked.forEach(listValues);
Now, if you're questioning whether you need to do this, yourself, the answer may well be no...
This exact thing is done by... ...every(?) library with higher-order features these days.
If you're using lodash or underscore or even jQuery, they're all going to have a way of taking a set of elements, and performing an action n-times.
If you aren't using such a thing, then by all means, write your own.
现在,如果你在质疑是否需要自己做这件事,答案很可能是否定的……
这件事是由……现在每个(?)具有高阶功能的库完成的。
如果您使用 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);
};
Update for ES6(ES2015) and Beyond
ES6(ES2015) 及更高版本的更新
Not only is a slice( )
/array( )
/etc helper method going to make life easier for people who want to use lists just like they use arrays (as they should), but for the people who have the luxury of operating in ES6+ browsers of the relatively-near future, or of "transpiling" in Babel today, you have language features built in, which make this type of thing unnecessary.
一个slice( )
/ array( )
/etc 辅助方法不仅会让那些想要像使用数组一样使用列表的人的生活更轻松(正如他们应该的那样),而且对于那些有幸在相对较近的 ES6+ 浏览器中操作的人来说未来,或者今天在 Babel 中“转译”,你有内置的语言特性,这使得这种类型的事情变得不必要。
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 ];
Super-clean, and very useful.
Look up the Restand Spreadoperators; try them out at the BabelJS site; if your tech stack is in order, use them in production with Babel and a build step.
超级干净,非常有用。
查找Rest和Spread运算符;在 BabelJS 站点上试用它们;如果你的技术栈是有序的,那么在 Babel 和构建步骤的生产中使用它们。
There's no good reason not to be able to use the transform from non-array into array... ...just don't make a mess of your code doing nothing butpasting that same ugly line, everywhere.
有没有好的理由不能够使用非数组转换成阵列......只是不要让你的代码乱七八糟的什么都不做,但粘贴同样丑陋线,无处不在。
回答by James Allardice
The querySelectorAll
methodreturns a NodeList
, which is similar to an array, but it's not quite an array. Therefore, it doesn't have a forEach
method (which array objects inherit via Array.prototype
).
该querySelectorAll
方法返回 a NodeList
,它类似于一个数组,但它不完全是一个数组。因此,它没有forEach
方法(数组对象通过 继承Array.prototype
)。
Since a NodeList
is similar to an array, array methods will actually work on it, so by using [].forEach.call
you are invoking the Array.prototype.forEach
method in the context of the NodeList
, as if you had been able to simply do yourNodeList.forEach(/*...*/)
.
由于 aNodeList
类似于数组,因此数组方法实际上可以处理它,因此通过使用,[].forEach.call
您是Array.prototype.forEach
在 的上下文中调用该方法NodeList
,就好像您已经能够简单地执行yourNodeList.forEach(/*...*/)
.
Note that the empty array literal is just a shortcut to the expanded version, which you will probably see quite often too:
请注意,空数组文字只是扩展版本的快捷方式,您可能也会经常看到:
Array.prototype.forEach.call(/*...*/);
回答by Michael Geary
The other answers have explained this code very well, so I'll just add a suggestion.
其他答案已经很好地解释了这段代码,所以我只添加一个建议。
This is a good example of code that should be refactored for simplicity and clarity. Instead of using [].forEach.call()
or Array.prototype.forEach.call()
every time you do this, make a simple function out of it:
这是一个很好的代码示例,为了简单和清晰,应该重构它。不要每次都使用[].forEach.call()
or ,而是使用Array.prototype.forEach.call()
它创建一个简单的函数:
function forEach( list, callback ) {
Array.prototype.forEach.call( list, callback );
}
Now you can call this function instead of the more complicated and obscure code:
现在你可以调用这个函数而不是更复杂和晦涩的代码:
forEach( document.querySelectorAll('a'), function( el ) {
// whatever with the current node
});
回答by Arun P Johny
It can be better written using
使用它可以更好地编写
Array.prototype.forEach.call( document.querySelectorAll('a'), function(el) {
});
What is does is document.querySelectorAll('a')
returns an object similar to an array, but it does not inherit from the Array
type.
So we calls the forEach
method from the Array.prototype
object with the context as the value returned by document.querySelectorAll('a')
它的作用是document.querySelectorAll('a')
返回一个类似于数组的对象,但它不从Array
类型继承。所以我们forEach
从Array.prototype
对象中调用该方法,并将上下文作为返回值document.querySelectorAll('a')
回答by Franti?ek He?a
Want to update on this old question:
想更新这个老问题:
The reason to use [].foreach.call()
to loop through elements in the modern browsers is mostly over. We can use document.querySelectorAll("a").foreach()
directly.
[].foreach.call()
在现代浏览器中使用循环元素的原因大部分已经结束。我们可以document.querySelectorAll("a").foreach()
直接使用。
NodeList objects are collections of nodes, usually returned by properties such as Node.childNodes and methods such as document.querySelectorAll().
Although NodeList is not an Array, it is possible to iterate over it with forEach(). It can also be converted to a real Array using Array.from().
However, some older browsers have not implemented NodeList.forEach() nor Array.from(). This can be circumvented by using Array.prototype.forEach() — see this document's Example.
NodeList 对象是节点的集合,通常由 Node.childNodes 等属性和 document.querySelectorAll() 等方法返回。
尽管 NodeList 不是数组,但可以使用 forEach() 对其进行迭代。也可以使用 Array.from() 将其转换为真正的 Array。
但是,一些较旧的浏览器还没有实现 NodeList.forEach() 和 Array.from()。这可以通过使用 Array.prototype.forEach() 来规避——参见本文档的示例。
回答by vava
[].forEach.call( document.querySelectorAll('a'), function(el) {
// whatever with the current node
});
It is basically the same as:
它与以下基本相同:
var arr = document.querySelectorAll('a');
arr.forEach(function(el) {
// whatever with the current node
});
回答by Alfredo Maria Milano
Just a quick and dirty solution I always end up using. I wouldn't touch prototypes, just as good practice. Of course, there are a lot of ways to make this better, but you get the idea.
只是我最终总是使用的一个快速而肮脏的解决方案。我不会碰原型,这也是一种很好的做法。当然,有很多方法可以让这一切变得更好,但你懂的。
const forEach = (array, callback) => {
if (!array || !array.length || !callback) return
for (var i = 0; i < array.length; i++) {
callback(array[i], i);
}
}
forEach(document.querySelectorAll('.a-class'), (item, index) => {
console.log(`Item: ${item}, index: ${index}`);
});
回答by Ted Hopp
An empty array has a property forEach
in its prototype which is a Function object. (The empty array is just an easy way to obtain a reference to the forEach
function that all Array
objects have.) Function objects, in turn, have a call
property which is also a function. When you invoke a Function's call
function, it runs the function with the given arguments. The first argument becomes this
in the called function.
一个空数组forEach
在其原型中有一个属性,它是一个 Function 对象。(空数组只是获取对forEach
所有Array
对象都具有的函数的引用的一种简单方法。)反过来,函数对象具有一个call
也是函数的属性。当您调用 Function 的call
函数时,它会使用给定的参数运行该函数。第一个参数this
在被调用的函数中。
You can find documentation for the call
function here. Documentation for forEach
is here.
回答by imos
Just add one line:
只需添加一行:
NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;
And voila!
瞧!
document.querySelectorAll('a').forEach(function(el) {
// whatever with the current node
});
Enjoy :—)
享受 :-)
Warning:NodeList is a global class. Don't use this recomendation if you writing public library. However it's very convenient way for increasing self-efficacy when you work on website or node.js app.
警告:NodeList 是一个全局类。如果您编写公共图书馆,请不要使用此建议。然而,当您在网站或 node.js 应用程序上工作时,这是提高自我效能的非常方便的方法。
回答by Mikel
Norguardexplained WHAT[].forEach.call()
does and James AllardiceWHYwe do it: because querySelectorAll returns a NodeList
that doesn't have a forEach method...
Norguard解释了WHAT[].forEach.call()
和James Allardice我们为什么这样做:因为 querySelectorAll 返回一个NodeList
没有 forEach 方法......
Unless you have modern browser like Chrome 51+, Firefox 50+, Opera 38, Safari 10.
除非你有像 Chrome 51+、Firefox 50+、Opera 38、Safari 10 这样的现代浏览器。
If not you can add a Polyfill:
如果没有,您可以添加一个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);
}
};
}