为什么Array.prototype.forEach不能链接? [英] Why can Array.prototype.forEach not be chained?

查看:154
本文介绍了为什么Array.prototype.forEach不能链接?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我今天知道 forEach()返回 undefined 。真是太浪费了!



如果它返回原始数组,它将会更加灵活,而不会破坏任何现有的代码。是否有任何理由 forEach 返回 undefined



是无论如何,要用 map & amp; amp; amp; amp; gt;等其他方法链接 forEach
$

  var obj = someThing.keys()
.filter(someFilter)
.forEach(passToAnotherObject)
.map(transformKeys)
.reduce(reduction )

因为 forEach 不想玩得很好,需要你再次运行 forEach 之前的所有方法来获取 forEach

解决方案

你想要的是方法级联通过方法链接。简单描述它们:


  1. 方法链接是指一个方法返回一个具有另一个方法的对象,并且您立即调用该方法。例如,使用jQuery:

      $(#person)
    .slideDown(slow)
    .addClass(分组)
    .css(margin-left,11px);


  2. 方法级联是指在同一对象上调用多个方法时。例如,在某些语言中,您可以这样做:

      foo 
    ..bar()
    ..巴兹();

    在JavaScript中相当于以下内容:

      foo.bar(); 
    foo.baz();


JavaScript没有任何特殊语法方法级联。但是,如果第一个方法调用返回 this ,则可以使用方法链接模拟方法级联。例如,如果 bar 返回这个(即 foo
$ p $ f
.bar()
.baz($ c>)然后链接相当于级联:

  foo 
.bar ();

一些方法,如过滤器和<$ c

另一方面, map 是可链接的,但不是可级联的,因为它们返回一个新数组,而不是原始数组。 code> forEach 函数不可链接,因为它不返回新对象。现在,问题出现在 forEach 应该是可级联还是不可用。



目前, forEach 不可级联。然而,这不是一个真正的问题,因为您可以简单地将中间数组的结果保存到一个变量中,然后使用它:

  var arr = someThing.keys()
.filter(someFilter);

arr.forEach(passToAnotherObject);

var obj = arr
.map(transformKeys)
.reduce(reduction);

是的,这个解决方案看起来比您想要的解决方案更丑陋。但是,由于以下几个原因,我比您的代码更喜欢它:


  1. 它是一致的,因为可链接方法不与可级联方法。因此,它促进了编程的功能风格(即编程时没有副作用)。

    级联本质上是一种有效的操作,因为您调用方法并忽略结果。因此,您将调用操作的副作用而不是结果。



    另一方面,可链式函数(如 map filter 没有任何副作用(如果他们的输入函数没有任何副作用)。他们仅用于他们的结果。



    在我的愚见中,混合可链式方法如 map for (如果它是可级联的)可级联函数的过滤器是亵渎,因为它会在其他纯转换中引入副作用。 / p>


  2. 它很明确。正如 Python的禅宗教导我们的那样,显式优于隐式”的方法级联只是语法糖。这是隐含的,它是有代价的。成本很复杂。

    现在,您可能会说我的代码看起来比您的代码复杂。如果是这样,你会在封面上判断这本书。在他们的着名论文走出焦油坑时,作者Ben Moseley和Peter Marks描述不同类型的软件复杂性。

    他们列表中的第二大软件复杂性是由<明确关注控制流引起的复杂性。例如:

      var obj = someThing.keys()
    .filter(someFilter)
    .forEach (passToAnotherObject)
    .map(transformKeys)
    .reduce(reduction);

    上面的程序明确地关注控制流,因为您明确指出 .forEach(passToAnotherObject)应该在 .map(transformKeys)之前发生,即使它不应该对整体转换产生任何影响。



    实际上,您可以将其完全从等式中删除,并且不会有任何区别:

      var obj = someThing.keys()
    .filter(someFilter)
    .map(transformKeys)
    .reduce(reduction);

    这表明 .forEach(passToAnotherObject)首先没有任何业务在等式中。因为它是一个副作用的操作,所以它应该与纯代码保持分开。



    当你像上面那样明确地写出它时,你不仅可以从侧面分离纯代码有效的代码,但你也可以选择何时评估每个计算。例如:

      var arr = someThing.keys()
    .filter(someFilter);

    var obj = arr
    .map(transformKeys)
    .reduce(reduction);

    arr.forEach(passToAnotherObject); //在纯计算后评估

    是的,您仍然明确地关注控制流。然而,至少现在你知道 .forEach(passToAnotherObject)与其他转换无关。



    因此,您已经消除了一些(但不是全部)由于明确关注控制流而导致的复杂性。


由于这些原因,我相信目前对 forEach 的实现实际上是有益的,因为它会阻止您编写由于明确关注控制流而引入复杂性的代码。



我从我过去在 BrowserStack 工作时的个人经验知道对控制流的明确关注在大规模软件应用中是一个大问题。这确实是一个真正的世界问题。

编写复杂代码很容易,因为复杂的代码通常是较短的(隐含的)代码。因此,在纯计算过程中放入一个副作用函数,如 forEach 总是很诱人,因为它需要更少的代码重构。



然而,从长远来看,它会让你的程序更加复杂。想想当你离开你工作的公司和其他人必须维护你的代码时,几年后会发生什么。您的代码现在看起来像:

  var obj = someThing.keys()
.filter(someFilter)
.forEach(passToAnotherObject)
.forEach(doSomething)
.map(transformKeys)
.forEach(doSomethingElse)
.reduce(reduction);

阅读代码的人现在必须假设所有额外的 forEach 方法是必不可少的,需要额外的工作来理解每个函数的作用,自己弄清楚这些额外的 forEach 方法并不是必须的计算 obj ,将它们从你的代码的心智模型中消除,只专注于基本部分。



这是一个很多不必要的复杂性添加到您的程序中,并且您认为这会让您的程序变得更加简单。


I learned today that forEach() returns undefined. What a waste!

If it returned the original array, it would be far more flexible without breaking any existing code. Is there any reason forEach returns undefined.

Is there anyway to chain forEach with other methods like map & filter?

For example:

var obj = someThing.keys()
.filter(someFilter)
.forEach(passToAnotherObject)
.map(transformKeys)
.reduce(reduction)

Wouldn't work because the forEach doesn't want to play nice, requiring you to run all the methods before the forEach again to get the object in the state needed for the forEach.

解决方案

What you want is known as method cascading via method chaining. Describing them in brief:

  1. Method chaining is when a method returns an object that has another method that you immediately invoke. For example, using jQuery:

    $("#person")
        .slideDown("slow")
        .addClass("grouped")
        .css("margin-left", "11px");
    

  2. Method cascading is when multiple methods are called on the same object. For example, in some languages you can do:

    foo
        ..bar()
        ..baz();
    

    Which is equivalent to the following in JavaScript:

    foo.bar();
    foo.baz();
    

JavaScript doesn't have any special syntax for method cascading. However, you can simulate method cascading using method chaining if the first method call returns this. For example, in the following code if bar returns this (i.e. foo) then chaining is equivalent to cascading:

foo
    .bar()
    .baz();

Some methods like filter and map are chainable but not cascadable because they return a new array, but not the original array.

On the other hand the forEach function is not chainable because it doesn't return a new object. Now, the question arises whether forEach should be cascadable or not.

Currently, forEach is not cascadable. However, that's not really a problem as you can simply save the result of the intermediate array in a variable and use that later:

var arr = someThing.keys()
    .filter(someFilter);

arr.forEach(passToAnotherObject);

var obj = arr
    .map(transformKeys)
    .reduce(reduction);

Yes, this solution looks uglier than the your desired solution. However, I like it more than your code for several reasons:

  1. It is consistent because chainable methods are not mixed with cascadable methods. Hence, it promotes a functional style of programming (i.e. programming with no side effects).

    Cascading is inherently an effectful operation because you are calling a method and ignoring the result. Hence, you're calling the operation for its side effects and not for its result.

    On the other hand, chainable functions like map and filter don't have any side effects (if their input function doesn't have any side effects). They are used solely for their results.

    In my humble opinion, mixing chainable methods like map and filter with cascadable functions like forEach (if it was cascadable) is sacrilege because it would introduce side effects in an otherwise pure transformation.

  2. It is explicit. As The Zen of Python teaches us, “Explicit is better than implicit.” Method cascading is just syntactic sugar. It is implicit and it comes at a cost. The cost is complexity.

    Now, you might argue that my code looks more complex than yours. If so, you would be judging the book by its cover. In their famous paper Out of the Tar Pit, the authors Ben Moseley and Peter Marks describe different types of software complexities.

    The second biggest software complexity on their list is complexity caused by explicit concern with control flow. For example:

    var obj = someThing.keys()
        .filter(someFilter)
        .forEach(passToAnotherObject)
        .map(transformKeys)
        .reduce(reduction);
    

    The above program is explicitly concerned with control flow because you are explicit stating that .forEach(passToAnotherObject) should happen before .map(transformKeys) even though it shouldn't have any effect on the overall transformation.

    In fact, you can remove it from the equation altogether and it wouldn't make any difference:

    var obj = someThing.keys()
        .filter(someFilter)
        .map(transformKeys)
        .reduce(reduction);
    

    This suggests that the .forEach(passToAnotherObject) didn't have any business being in the equation in the first place. Since it's a side effectful operation, it should be kept separate from pure code.

    When you write it explicitly as I did above, not only are you separating pure code from side effectful code but also you can choose when to evaluate each computation. For example:

    var arr = someThing.keys()
        .filter(someFilter);
    
    var obj = arr
        .map(transformKeys)
        .reduce(reduction);
    
    arr.forEach(passToAnotherObject); // evaluate after pure computation
    

    Yes, you are still explicitly concerned with control flow. However, at least now you know that .forEach(passToAnotherObject) has nothing to do with the other transformations.

    Thus, you have eliminated some (but not all) of the complexity caused by explicit concern with control flow.

For these reasons, I believe that the current implementation of forEach is actually beneficial because it prevents you from writing code that introduces complexity due to explicit concern with control flow.

I know from personal experience from when I used to work at BrowserStack that explicit concern with control flow is a big problem in large-scale software applications. It is indeed a real world problem.

It's easy to write complex code because complex code is usually shorter (implicit) code. So it's always tempting to drop in a side effectful function like forEach in the middle of a pure computation because it requires less code refactoring.

However, in the long run it makes your program more complex. Think of what would happen a few years down the line when you quit the company that you work for and somebody else has to maintain your code. Your code now looks like:

var obj = someThing.keys()
    .filter(someFilter)
    .forEach(passToAnotherObject)
    .forEach(doSomething)
    .map(transformKeys)
    .forEach(doSomethingElse)
    .reduce(reduction);

The person reading your code now has to assume that all the additional forEach methods in your chain are essential, put in extra work to understand what each function does, figure out by herself that these extra forEach methods are not essential to compute obj, eliminate them from her mental model of your code and only concentrate on the essential parts.

That's a lot of unnecessary complexity added to your program, and you thought that it was making your program more simple.

这篇关于为什么Array.prototype.forEach不能链接?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆