[].forEach.call() 在 JavaScript 中做了什么?编辑

我正在查看一些代码片段,我发现多个元素在节点列表上调用函数,并将forEach应用于空数组。

例如,我有这样的东西:

[].forEach.call( document.querySelectorAll('a'), function(el) {
   // whatever with the current node
});

但我不明白它是如何工作的。谁能解释一下前台空数组的行为以及它是如何工作的?call


答案 1

[]是一个数组。
根本不使用此数组。

它被放在页面上,因为使用数组可以让你访问数组原型,比如..forEach

这比打字更快Array.prototype.forEach.call(...);

接下来,是一个将函数作为输入的函数...forEach

[1,2,3].forEach(function (num) { console.log(num); });

...对于 中的每个元素(其中是类似数组的,因为它有一个,你可以像)一样访问它的部分,它将传递三件事:thisthislengththis[1]

  1. 数组中的元素
  2. 元素的索引(第三个元素将通过2)
  3. 对数组的引用

最后,是函数具有的原型(它是在其他函数上调用的函数)。
将采用它的第一个参数,并在常规函数内部替换你传递的任何内容,作为第一个参数(或者将在日常JS中使用,或者如果处于“严格模式”,则将是你传递的任何内容)。其余参数将传递给原始函数。.call.callthiscallundefinednullwindow

[1, 2, 3].forEach.call(["a", "b", "c"], function (item, i, arr) {
    console.log(i + ": " + item);
});
// 0: "a"
// 1: "b"
// 2: "c"

因此,您正在创建一种调用函数的快速方法,并且您将从空数组更改为所有标记的列表,并且对于每个按顺序,您将调用提供的函数。forEachthis<a><a>

编辑

逻辑结论/清理

下面,有一篇文章的链接,建议我们放弃函数式编程的尝试,每次都坚持手动,内联循环,因为这个解决方案是黑客式的,难看的。

我想说的是,虽然不如它的对应物有用,但,,当你真正想做的只是修改外部世界(而不是数组)n次,同时可以访问或时,它仍然服务于目的。.forEach.map(transformer).filter(predicate).reduce(combiner, initialValue)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);

现在,如果你质疑自己是否需要这样做,答案很可能是否定的......
正是这件事是......every(?) 库现在具有更高阶的功能。
如果你使用的是 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) 及其他版本的更新

//etc帮助器方法不仅会让那些想要使用列表的人的生活更轻松,就像他们使用数组一样(他们应该这样做),而且对于那些有幸在相对不久的将来的ES6 +浏览器中操作的人来说,或者在今天的Babel中“转译”,你有内置的语言功能, 这使得这种事情变得不必要。slice( )array( )

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 ];

超级干净,非常有用。
查找 RestSpread 运算符;在BabelJS网站上尝试一下;如果您的技术堆栈井然有序,请在生产中使用 Babel 和构建步骤。


没有充分的理由不能使用从非数组到数组的转换......只是不要把你的代码弄得一团糟,什么都不做,只是到处粘贴那条丑陋的线。


答案 2

querySelectorAll 方法返回一个 ,它类似于一个数组,但它不完全是一个数组。因此,它没有方法(数组对象通过 继承)。NodeListforEachArray.prototype

由于 a 类似于数组,因此数组方法实际上将在其上工作,因此通过使用,您可以在 的上下文中调用该方法,就好像您能够简单地执行 。NodeList[].forEach.callArray.prototype.forEachNodeListyourNodeList.forEach(/*...*/)

请注意,空数组文本只是扩展版本的快捷方式,您可能也会经常看到:

Array.prototype.forEach.call(/*...*/);