我正在查看一些代码片段,发现多个元素在节点列表上调用函数,并将 forEach 应用于空数组。
例如我有类似的东西:
[].forEach.call( document.querySelectorAll('a'), function(el) {
// whatever with the current node
});
但我不明白它是如何工作的。谁能解释一下 forEach 前面的空数组的行为以及它是如何call
工作的?
我正在查看一些代码片段,发现多个元素在节点列表上调用函数,并将 forEach 应用于空数组。
例如我有类似的东西:
[].forEach.call( document.querySelectorAll('a'), function(el) {
// whatever with the current node
});
但我不明白它是如何工作的。谁能解释一下 forEach 前面的空数组的行为以及它是如何call
工作的?
[]
是一个数组。
这个数组根本不用。
它被放在页面上,因为使用数组可以让您访问数组原型,例如.forEach
.
这比打字快Array.prototype.forEach.call(...);
接下来,forEach
是一个将函数作为输入的函数......
[1,2,3].forEach(function (num) { console.log(num); });
...对于每个元素this
(其中this
类似于数组,因为它有 alength
并且您可以访问它的部分,如this[1]
),它将传递三件事:
2
)最后,.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"
因此,您正在创建一种调用forEach
函数的快速方法,并且您正在this
从空数组更改为所有<a>
标签的列表,并且对于每个<a>
顺序,您正在调用提供的函数。
下面有一篇文章的链接,建议我们放弃函数式编程的尝试,每次都坚持手动的内联循环,因为这种解决方案是 hack-ish 且难看。
我想说虽然.forEach
它的帮助不如它的对应物 , .map(transformer)
, .filter(predicate)
,.reduce(combiner, initialValue)
但当你真正想做的只是修改外部世界(而不是数组)n 次,同时可以访问arr[i]
or时,它仍然有用i
。
那么我们如何处理这种差异,因为 Motto 显然是一个有才华和知识渊博的人,我想想象我知道我在做什么/我要去哪里(不时......其他次是头头学习)?
答案实际上很简单,由于疏忽,鲍勃叔叔和克罗克福德爵士都会面对:
清理它。
function toArray (arrLike) { // or asArray(), or array(), or *whatever*
return [].slice.call(arrLike);
}
var checked = toArray(checkboxes).filter(isChecked);
checked.forEach(listValues);
现在,如果您质疑自己是否需要这样做,答案很可能是否定的……
这件事是由……现在每个(?)具有高阶特性的库完成的。
如果您使用 lodash 或 underscore 甚至 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);
};
slice( )
/ /etc 辅助方法不仅array( )
可以让那些想要像使用数组一样使用列表的人生活更轻松(因为他们应该),而且对于那些可以在相对较近的 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 ];
超级干净,非常好用。
查找Rest和Spread运算符;在 BabelJS 网站上试用它们;如果您的技术堆栈是有序的,请使用 Babel 和构建步骤在生产中使用它们。
没有充分的理由不能使用从非数组到数组的转换......只是不要把你的代码弄得一团糟,除了在任何地方粘贴同样丑陋的行之外什么都不做。
该querySelectorAll
方法返回 a NodeList
,它类似于数组,但不完全是数组。因此,它没有forEach
方法(哪些数组对象通过 继承Array.prototype
)。
由于 aNodeList
类似于数组,因此数组方法实际上将对其起作用,因此通过使用,您可以在 的上下文中[].forEach.call
调用该方法,就好像您能够简单地执行.Array.prototype.forEach
NodeList
yourNodeList.forEach(/*...*/)
请注意,空数组文字只是扩展版本的快捷方式,您可能也会经常看到:
Array.prototype.forEach.call(/*...*/);
其他答案已经很好地解释了这段代码,所以我只是添加一个建议。
这是一个很好的代码示例,应该为了简单和清晰而重构。不要使用[].forEach.call()
orArray.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
类型继承。所以我们forEach
从对象调用方法,Array.prototype
上下文作为返回的值document.querySelectorAll('a')
[].forEach.call( document.querySelectorAll('a'), function(el) {
// whatever with the current node
});
它与以下内容基本相同:
var arr = document.querySelectorAll('a');
arr.forEach(function(el) {
// whatever with the current node
});
此页面上有很多很好的信息(请参阅答案+答案+评论),但我最近遇到了与 OP 相同的问题,并且需要进行一些挖掘才能获得全貌。所以,这是一个简短的版本:
目标是在本身没有这些方法Array
的类数组上使用方法。NodeList
一个较旧的模式通过 选择了 Array 的方法Function.call()
,并使用了数组文字 ( []
),而不是Array.prototype
因为它的类型更短:
[].forEach.call(document.querySelectorAll('a'), a => {})
一种较新的模式(ECMAScript 2015 后)是使用Array.from()
:
Array.from(document.querySelectorAll('a')).forEach(a => {})
想更新这个老问题:
[].foreach.call()
在现代浏览器中使用循环元素的原因已经基本结束。我们可以document.querySelectorAll("a").foreach()
直接使用。
NodeList 对象是节点的集合,通常由 Node.childNodes 等属性和 document.querySelectorAll() 等方法返回。
尽管 NodeList 不是数组,但可以使用 forEach() 对其进行迭代。也可以使用 Array.from() 将其转换为真正的 Array。
但是,一些较旧的浏览器没有实现 NodeList.forEach() 或 Array.from()。这可以通过使用 Array.prototype.forEach() 来规避——请参阅本文档的示例。
只需添加一行:
NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;
瞧!
document.querySelectorAll('a').forEach(function(el) {
// whatever with the current node
});
享受 :-)
警告: NodeList 是一个全局类。如果您编写公共图书馆,请不要使用此建议。但是,当您在网站或 node.js 应用程序上工作时,这是提高自我效能的一种非常方便的方法。
只是一个快速而肮脏的解决方案,我总是最终使用。我不会碰原型,就像好的做法一样。当然,有很多方法可以让它变得更好,但你明白了。
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}`);
});
[]
总是返回一个新数组,它等效于new Array()
但保证返回一个数组,因为Array
可以被用户覆盖而[]
不能。所以这是获取 的原型的安全方法Array
,然后如前所述,call
用于在类似数组的节点列表 (this) 上执行函数。
使用给定的 this 值和单独提供的参数调用函数。mdn
Norguard解释了做什么 [].forEach.call()
和James Allardice 为什么我们这样做:因为 querySelectorAll 返回一个NodeList
没有 forEach 方法的...
除非您拥有现代浏览器,例如 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);
}
};
}