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

时间:2013-04-17 06:48:25

标签: javascript arrays foreach ecmascript-5 nodelist

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

例如,我有类似的东西:

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

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

12 个答案:

答案 0 :(得分:173)

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

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

这比输入Array.prototype.forEach.call(...);

更快

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

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

...并且对于this中的每个元素(其中this类似于数组,因为它有一个length,您可以访问其this[1]部分)它将通过三件事:

  1. 数组中的元素
  2. 元素的索引(第三个元素将通过2
  3. 对数组的引用
  4. 最后,.call是函数的原型(它是在其他函数上调用的函数)。
    .call将使用其第一个参数,并将常规函数内的this替换为您传递的任何内容call,因为第一个参数(undefinednull将使用在日常的JS中window,或者如果在“严格模式”下,将是你传递的任何内容。其余参数将传递给原始函数。

    [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>按顺序,您正在调用提供的函数。

    修改

    逻辑结论/清理

    下面是一篇文章的链接,建议我们废弃功能编程的尝试,并且每次都坚持手动,内联循环,因为这个解决方案是黑客和不雅观的。

    我会说.forEach虽然没有.map(transformer) .filter(predicate).reduce(combiner, initialValue)arr[i]那么有用,但当你真正想做的事情时它仍然有用修改外部世界(不是数组),n次,同时可以访问ifunction toArray (arrLike) { // or asArray(), or array(), or *whatever* return [].slice.call(arrLike); } var checked = toArray(checkboxes).filter(isChecked); checked.forEach(listValues);

    那么我们如何处理这种差异,因为座右铭显然是一个才华横溢,知识渊博的人,我想我想知道我在做什么/我要去哪里(时不时......)。 ..其他时间它是首先学习的?)

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

    清理

    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);
    };
    

    现在,如果你在质疑自己是否需要这样做,那么答案可能就是没有...... 这一天确实是由......每个(?)库具有更高阶的特征来完成的 如果你正在使用lodash或下划线甚至是jQuery,那么他们都将采用一种方法来获取一组元素,并执行n次动作。
    如果你没有使用这样的东西,那么一定要写下你自己的东西。

    slice( )

    ES6(ES2015)和Beyond

    的更新

    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 ]; / etc辅助方法不仅可以让想要使用列表的人更轻松,就像他们使用数组一样(对于他们应该使用的),但是对于那些在相对较近的未来的ES6 +浏览器中运行,或者今天在Babel中进行“转换”,你有内置的语言功能,这使得这种类型的东西变得不必要。

    {{1}}

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


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

答案 1 :(得分:45)

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

由于NodeList类似于数组,因此数组方法实际上会对其进行处理,因此通过使用[].forEach.call,您将在{{1}的上下文中调用Array.prototype.forEach方法,就像你能够简单地做NodeList

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

yourNodeList.forEach(/*...*/)

答案 2 :(得分:20)

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

这是代码的一个很好的例子,为了简单和清晰起见,应该重构这些代码。每次执行此操作时,不要使用[].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
});

答案 3 :(得分:4)

可以使用

更好地编写
Array.prototype.forEach.call( document.querySelectorAll('a'), function(el) {

});

做什么是document.querySelectorAll('a')返回一个类似于数组的对象,但它不会从Array类型继承。 因此,我们从forEach对象调用Array.prototype方法,并将上下文作为document.querySelectorAll('a')返回的值

答案 4 :(得分:4)

[].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
});

答案 5 :(得分:1)

空数组的原型中有一个属性forEach,它是一个Function对象。 (空数组只是获取所有forEach个对象所具有的Array函数的引用的简单方法。)函数对象又具有call属性,该属性也是功能。当您调用Function的call函数时,它会使用给定的参数运行函数。第一个参数在被调用函数中变为this

您可以找到call函数here的文档。 forEach的文档为here

答案 6 :(得分:1)

只需添加一行:

NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;

瞧!

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

享受: - )

警告: NodeList是一个全局类。如果您编写公共图书馆,请不要使用此推荐。但是,当您在网站或node.js应用程序上工作时,这是提高自我效能的非常方便的方法。

答案 7 :(得分:1)

我总是最终使用一种快速而肮脏的解决方案。我不会碰原型,只是一个好的实践。当然,有很多方法可以使它变得更好,但是您明白了。

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}`);
});

答案 8 :(得分:0)

[]总是返回一个新数组,它相当于new Array()但保证返回一个数组,因为Array可能被用户覆盖,而[]不能。所以这是获取Array原型的一种安全方法,然后如上所述,call用于执行arraylike nodelist(this)上的函数。

  

使用给定的此值和参数调用函数   个别。 mdn

答案 9 :(得分:0)

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);
        }
    };
}

答案 10 :(得分:0)

想更新这个老问题:

在现代浏览器中使用[].foreach.call()循环浏览元素的原因已基本结束。我们可以直接使用document.querySelectorAll("a").foreach()

  

NodeList对象是节点的集合,通常由   属性(例如Node.childNodes)和方法(例如   document.querySelectorAll()。

     

尽管NodeList不是数组,但可以对其进行迭代   使用forEach()。也可以使用以下命令将其转换为真实数组   Array.from()。

     

但是,某些较旧的浏览器尚未实现NodeList.forEach()   也不是Array.from()。可以通过使用以下方法来规避   Array.prototype.forEach()—请参阅本文档的示例。

答案 11 :(得分:0)

此页面上有很多很好的信息(请参阅answer + answer + comment),但是我最近遇到了与OP相同的问题,并且花了一些时间才能获得整个图片。所以,这是一个简短的版本:

目标是在本身没有这些方法的类似数组Array上使用NodeList方法。

一个较旧的模式通过Function.call()选择了Array的方法,并使用了数组文字([]而不是Array.prototype,因为它的类型更短:

[].forEach.call(document.querySelectorAll('a'), a => {})

较新的模式(在ECMAScript 2015之后)将使用Array.from()

Array.from(document.querySelectorAll('a')).forEach(a => {})