Javascript 为什么将匿名函数存储为变量并将其传递给高阶函数很有用?

Javascript 为什么将匿名函数存储为变量并将其传递给高阶函数很有用?,javascript,functional-programming,higher-order-functions,Javascript,Functional Programming,Higher Order Functions,我正在努力理解函数式编程,有一个问题 在本例中*一个匿名函数被分配给变量isDog,然后isDog被传递给filter。然后我们可以将动物数组中的所有狗过滤成一个新数组。(我知道我可以缩短这段代码,但这不是帖子的重点:)) 我知道函数可以作为参数传递,可能通过将函数分配给变量来显式传递 但是现在匿名函数是一个变量,我们不使用括号来编写它,即isDog(),直观地看,这似乎降低了代码的可读性。乍一看,我认为isDog只是一个变量,而不是一个函数 const isDog = ({species})

我正在努力理解函数式编程,有一个问题

在本例中*一个匿名函数被分配给变量isDog,然后isDog被传递给filter。然后我们可以将动物数组中的所有狗过滤成一个新数组。(我知道我可以缩短这段代码,但这不是帖子的重点:))

我知道函数可以作为参数传递,可能通过将函数分配给变量来显式传递

但是现在匿名函数是一个变量,我们不使用括号来编写它,即isDog(),直观地看,这似乎降低了代码的可读性。乍一看,我认为isDog只是一个变量,而不是一个函数

const isDog = ({species}) => species === 'dog'
const isCat = ({species}) => species === 'cat'

// ex1
let allDogs = animals.filter(isDog)

// ex2
if (isDog(x)) {
  throw Error('Dogs are not allowed !')
}

// ex3
function canCooperate (a,b) {
  switch (true) {
    case isCat(a) && isDog(b):
    case isDog(a) && isCat(b):
      return false
    default:
      return true
  }
}
即使我们可以推断它是一个函数,因为它连接到过滤器,它仍然令人困惑,我假设还有其他情况下它不明显。所以现在我们必须查看isDog是/做什么的

所以我要问的是为什么要这样做?没有括号的isDog看起来确实更优雅,但是有什么技术原因可以这样使用它吗

注意,我理解函数作为参数的威力,我的问题更多的是,如果变量会产生歧义代码,为什么要将它们赋给变量

谢谢


*改编自这段有用的视频。大约8分钟,

只有在调用函数时括号才在那里。如果将带括号的调用传递到.filter()方法中,实际上将传递调用的结果,而不是指向调用本身的指针。另一种方法是将整个函数传递到过滤器函数中,但可读性往往会受到影响。它还限制了您修改要放入函数的内容的能力

想象一下,在这种情况下,您可能希望执行排序而不是过滤器,并且希望根据传入的布尔值更改排序

以这一基本分类为例:

var numbers = [4, 2, 5, 1, 3];

numbers.sort(function(a, b) {
  return a - b;
});
var fiveMinutesAsMilliseconds = 5 * SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;
如果您希望能够选择根据另一个变量对其进行排序的方向,该怎么办。您可以定义这两个排序函数,然后根据需要传递正确的排序函数

var numbers = [4, 2, 5, 1, 3];
var sortAscending = function(a, b) {
  return a - b;
}

var sortDescending = function(a, b) {
  return b - a;
}

function doSort(myArray, dir) {
    var sortMethod = (dir == "asc") ? sortAscending : sortDescending;
    myArray.sort(sortMethod );
}

doSort(numbers, "asc");

上面说明了当您需要时,以这种方式传递方法调用如何允许更大的灵活性,并确保仅当在sort内部执行时才进行调用。

括号仅在调用函数时出现。如果将带括号的调用传递到.filter()方法中,实际上将传递调用的结果,而不是指向调用本身的指针。另一种方法是将整个函数传递到过滤器函数中,但可读性往往会受到影响。它还限制了您修改要放入函数的内容的能力

想象一下,在这种情况下,您可能希望执行排序而不是过滤器,并且希望根据传入的布尔值更改排序

以这一基本分类为例:

var numbers = [4, 2, 5, 1, 3];

numbers.sort(function(a, b) {
  return a - b;
});
var fiveMinutesAsMilliseconds = 5 * SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;
如果您希望能够选择根据另一个变量对其进行排序的方向,该怎么办。您可以定义这两个排序函数,然后根据需要传递正确的排序函数

var numbers = [4, 2, 5, 1, 3];
var sortAscending = function(a, b) {
  return a - b;
}

var sortDescending = function(a, b) {
  return b - a;
}

function doSort(myArray, dir) {
    var sortMethod = (dir == "asc") ? sortAscending : sortDescending;
    myArray.sort(sortMethod );
}

doSort(numbers, "asc");

上面说明了当您需要方法调用时,以这种方式传递方法调用如何提供更大的灵活性,并确保仅在排序内部执行时才进行调用。

仅当您打算多次使用该函数时,将匿名函数分配给变量才有意义。否则,您可以将其作为参数直接传递给
filter()
调用。另外,当使用像
filter()
这样的方法时,最好使用。另外,如果您不打算稍后重新分配变量,最好使用
const
而不是
var
——这将防止您错误地重新分配该变量。看

为了解决您对为什么
isDog
没有方括号的困惑:方括号表示调用函数,而这里您只是将此函数作为参数传递
filter(isDog())
意味着调用
isDog
函数,然后将返回值作为参数传递给
filter()
方法。只有当
isDog
返回另一个函数时,这才有意义

const isDog = ({species}) => species === 'dog'
const isCat = ({species}) => species === 'cat'

// ex1
let allDogs = animals.filter(isDog)

// ex2
if (isDog(x)) {
  throw Error('Dogs are not allowed !')
}

// ex3
function canCooperate (a,b) {
  switch (true) {
    case isCat(a) && isDog(b):
    case isDog(a) && isCat(b):
      return false
    default:
      return true
  }
}
您的代码可能如下所示:

const dogs = animals.filter(animal => animal.species === 'dog')

只有当您打算多次使用匿名函数时,才可以将该函数赋值给变量。否则,您可以将其作为参数直接传递给
filter()
调用。另外,当使用像
filter()
这样的方法时,最好使用。另外,如果您不打算稍后重新分配变量,最好使用
const
而不是
var
——这将防止您错误地重新分配该变量。看

为了解决您对为什么
isDog
没有方括号的困惑:方括号表示调用函数,而这里您只是将此函数作为参数传递
filter(isDog())
意味着调用
isDog
函数,然后将返回值作为参数传递给
filter()
方法。只有当
isDog
返回另一个函数时,这才有意义

const isDog = ({species}) => species === 'dog'
const isCat = ({species}) => species === 'cat'

// ex1
let allDogs = animals.filter(isDog)

// ex2
if (isDog(x)) {
  throw Error('Dogs are not allowed !')
}

// ex3
function canCooperate (a,b) {
  switch (true) {
    case isCat(a) && isDog(b):
    case isDog(a) && isCat(b):
      return false
    default:
      return true
  }
}
您的代码可能如下所示:

const dogs = animals.filter(animal => animal.species === 'dog')

我们将函数分配给变量的原因与将值分配给变量的原因相同。变量是带有标签的意图描述符

如果您阅读
anists.filter(isDog)
,应该可以清楚地看到该函数的预期效果

const isDog = ({species}) => species === 'dog'
const isCat = ({species}) => species === 'cat'

// ex1
let allDogs = animals.filter(isDog)

// ex2
if (isDog(x)) {
  throw Error('Dogs are not allowed !')
}

// ex3
function canCooperate (a,b) {
  switch (true) {
    case isCat(a) && isDog(b):
    case isDog(a) && isCat(b):
      return false
    default:
      return true
  }
}
也就是说,代码中的期望是您将动物的集合过滤仅包括是狗的动物

这与任何其他值的变量用法没有什么不同

以下面这行为例:

var numbers = [4, 2, 5, 1, 3];

numbers.sort(function(a, b) {
  return a - b;
});
var fiveMinutesAsMilliseconds = 5 * SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;
你应该能读到