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

Mim*_*imo 118 javascript arrays foreach nodelist ecmascript-5

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

例如,我有类似的东西:

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

但我无法理解它是如何工作的.任何人都可以解释我在forEach前面的空数组的行为以及它是如何call工作的?

Nor*_*ard 188

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

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

这比键入更快 Array.prototype.forEach.call(...);

接下来,forEach是一个功能作为输入...

[1,2,3].forEach(function (num) { console.log(num); });
Run Code Online (Sandbox Code Playgroud)

...对于每个元素this(在this类似数组的地方,因为它有一个length,你可以访问它的部分this[1]),它将传递三件事:

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

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

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

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

编辑

逻辑结论/清理

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

我想说的是,虽然.forEach比其同行少乐于助人,.map(transformer),.filter(predicate),.reduce(combiner, initialValue),它仍然成为目的时,你真正想要做的是改变外界(不是数组),n次,虽然能获得两种arr[i]i.

所以我们如何处理这种差异,因为座右铭显然是一个才华横溢,知识渊博的家伙,我想我想知道我在做什么/我要去哪里(时不时......等等)这是第一次学习)?

答案实际上非常简单,由于疏忽,鲍勃叔叔和克罗克福德爵士都将面临这样的问题:

清理它.

function toArray (arrLike) { // or asArray(), or array(), or *whatever*
  return [].slice.call(arrLike);
}

var checked = toArray(checkboxes).filter(isChecked);
checked.forEach(listValues);
Run Code Online (Sandbox Code Playgroud)

现在,如果你在质疑自己是否需要这样做,答案可能
就是否定...... 这一天确实是......每个(?)库具有更高阶的功能.
如果你正在使用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);
};
Run Code Online (Sandbox Code Playgroud)

ES6(ES2015)和Beyond的更新

对于想要使用列表的人来说,使用数组(就像他们应该的那样)不仅可以使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 ];
Run Code Online (Sandbox Code Playgroud)

超级干净,非常实用.
查看RestSpread运算符; 在BabelJS网站上试用它们; 如果您的技术堆栈符合规定,请使用Babel和构建步骤在生产中使用它们.


有没有好的理由不能够使用非阵列到阵列变换......只是不要让你的代码什么都不做的一塌糊涂,但粘贴同样丑陋线,无处不在.

  • @MuhammadSaleh` .call`不会改变你传递给`forEach`的`this`的值,`.call`会改变forEach*里面`this`*的值.如果你对为什么`this`没有从`forEach`传递到你想调用的函数感到困惑,你应该在JavaScript中查找`this`的行为.作为附录,仅适用于此(和map/filter/reduce),`forEach`有第二个参数,它在函数`arr.forEach(fn,thisInFn)`或`[] .forEach中设置`this` .call(arrLike,fn,thisInFn);`或者只是使用`.bind`; `arr.forEach(fn.bind(thisInFn));` (2认同)

Jam*_*ice 48

querySelectorAll方法返回一个NodeList类似于数组的a,但它不是一个数组.因此,它没有一个forEach方法(数组对象继承通过Array.prototype).

由于a NodeList类似于数组,因此数组方法实际上会对其进行处理,因此通过使用,[].forEach.call您可以Array.prototype.forEach在上下文中调用该方法NodeList,就像您已经能够简单地执行一样yourNodeList.forEach(/*...*/).

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

Array.prototype.forEach.call(/*...*/);
Run Code Online (Sandbox Code Playgroud)

  • 因此,澄清一下,使用`[] .forEach.call(['a','b'],cb)`而不是'''','b'].forEach(cb)`在每天都没有优势使用标准数组的应用程序,只是在尝试迭代在自己的原型上没有`forEach`的类似数组的结构时?那是对的吗? (6认同)
  • @MattFletcher:是的,这是对的.两者都有效,但为什么会使事情过于复杂?只需直接在数组本身上调用该方法即可. (2认同)

Mic*_*ary 20

其他答案很好地解释了这段代码,所以我只想添加一个建议.

这是代码的一个很好的例子,为了简单和清晰起见,应该重构这些代码.而不是使用[].forEach.call()Array.prototype.forEach.call()每次执行此操作,而不是使用它来创建一个简单的函数:

function forEach( list, callback ) {
    Array.prototype.forEach.call( list, callback );
}
Run Code Online (Sandbox Code Playgroud)

现在你可以调用这个函数而不是更复杂和模糊的代码:

forEach( document.querySelectorAll('a'), function( el ) {
   // whatever with the current node
});
Run Code Online (Sandbox Code Playgroud)


vav*_*ava 7

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

它基本上与以下相同:

var arr = document.querySelectorAll('a');
arr.forEach(function(el) {
   // whatever with the current node
});
Run Code Online (Sandbox Code Playgroud)


Aru*_*hny 5

最好用

Array.prototype.forEach.call( document.querySelectorAll('a'), function(el) {

});
Run Code Online (Sandbox Code Playgroud)

所执行的操作是document.querySelectorAll('a')返回与数组相似的对象,但它不会从Array类型继承。因此,我们forEachArray.prototype对象中调用方法,并将上下文作为返回的值document.querySelectorAll('a')