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

[].forEach.call()在JavaScript中做什么?,javascript,arrays,foreach,ecmascript-5,nodelist,Javascript,Arrays,Foreach,Ecmascript 5,Nodelist,我查看了一些代码片段,发现多个元素在节点列表上调用函数,并将forEach应用于空数组 例如,我有一些类似于: [].forEach.call( document.querySelectorAll('a'), function(el) { // whatever with the current node }); 但我不明白它是怎么工作的。有人能给我解释一下forEach前面的空数组的行为以及调用如何工作吗?空数组的原型中有一个属性forEach,它是一个函数对象。(空数组只是获取所有a

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

例如,我有一些类似于:

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

但我不明白它是怎么工作的。有人能给我解释一下forEach前面的空数组的行为以及
调用如何工作吗?

空数组的原型中有一个属性
forEach
,它是一个函数对象。(空数组只是获取所有
array
对象都具有的
forEach
函数引用的一种简单方法。)函数对象反过来又具有一个
call
属性,该属性也是一个函数。调用函数的
调用
函数时,它使用给定参数运行函数。第一个参数在被调用函数中变为
this

您可以找到
调用
函数的文档。
forEach
的文档是。

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

由于
NodeList
类似于数组,数组方法实际上会对其起作用,因此通过使用
[].forEach.call
可以在
NodeList
的上下文中调用
array.prototype.forEach
方法,就好像您能够简单地执行
您的NodeList.forEach(/*…*/)

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

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

它可以更好地使用

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

});
is所做的是
document.querySelectorAll('a')
返回一个类似于数组的对象,但它不从
数组
类型继承。
因此,我们从
数组调用
forEach
方法。prototype
对象的上下文是
文档返回的值。querySelectorAll('a')
[]
是一个数组。
这个数组根本不用

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

这比键入
Array.prototype.forEach.call(…)要快得多

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

[1,2,3].forEach(function (num) { console.log(num); });
…对于
this
中的每个元素(其中
this
类似于数组,因为它具有
长度
,您可以访问它的部分,如
this[1]
),它将传递三个信息:

  • 数组中的元素
  • 元素的索引(第三个元素将通过
    2
  • 对数组的引用
  • 最后,
    .call
    是函数拥有的原型(它是在其他函数上被调用的函数)。
    .call
    将取其第一个参数,并用您传递的
    call
    替换常规函数中的
    ,作为第一个参数(
    未定义的
    将在日常JS中使用
    窗口
    ,或是您传递的任何内容,如果处于“严格模式”)。其余参数将传递给原始函数

    [1, 2, 3].forEach.call(["a", "b", "c"], function (item, i, arr) {
        console.log(i + ": " + item);
    });
    // 0: "a"
    // 1: "b"
    // 2: "c"
    
    因此,您正在创建一种调用
    forEach
    函数的快速方法,并且您正在将
    从空数组更改为所有
    标记的列表,并且对于每个
    按顺序调用提供的函数

    编辑 逻辑结论/清理 下面是一篇文章的链接,建议我们放弃函数式编程的尝试,每次都坚持手动、内联循环,因为这种解决方案既有黑客味又难看

    我要说的是,虽然
    .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);
    
    现在,如果你问自己是否需要这样做,答案很可能是否定的。
    这件事是由。。。如今,每个(?)库都具有更高阶的功能。
    如果您使用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)及以后的更新 不仅是
    slice()
    /
    array()
    /etc助手方法将使那些希望像使用数组一样使用列表的人(他们应该这样做)的生活变得更轻松,而且对于那些在相对不远的将来有幸在ES6+浏览器中操作,或者今天有幸在Babel中使用“transpiling”的人来说,你有内置的语言特性,这使得这类事情变得不必要

    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网站上试用;如果您的技术堆栈处于有序状态,请在生产中与巴贝尔和构建步骤一起使用它们



    没有理由不使用从非数组到数组的转换。。。不要把你的代码弄得一团糟,除了到处粘贴同样难看的行。

    []
    总是返回一个新数组,它相当于
    新数组()
    
    function forEach( list, callback ) {
        Array.prototype.forEach.call( list, callback );
    }
    
    forEach( document.querySelectorAll('a'), function( el ) {
       // whatever with the current node
    });
    
    NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;
    
    document.querySelectorAll('a').forEach(function(el) {
      // whatever with the current node
    });
    
    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);
            }
        };
    }
    
    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}`);
    });
    
    [].forEach.call(document.querySelectorAll('a'), a => {})
    
    Array.from(document.querySelectorAll('a')).forEach(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
    });