【问题标题】:js - Higher order unary function - why is this needed?js - 高阶一元函数 - 为什么需要这个?
【发布时间】:2026-02-06 03:20:12
【问题描述】:

从这里开始:下面的 sn-p 不能按预期工作:

['1', '2', '3'].map(parseInt)           // [1, NaN, NaN] <- bad output

一个简单的改变就可以解决它:

['1', '2', '3'].map(a => parseInt(a))   // [ 1, 2, 3 ] <- good output


但我正在阅读的这个 book 有不同的补救措施:

创建一个unary 函数,它接受一个函数(在我们的例子中是parseInt),如果该函数有n 参数,它会返回一个只有一个参数的新函数。这本书是这样写的:

const unary = (f) => {
  return f.length === 1 ? f : (args) => f(args)
}

['1', '2', '3'].map(unary(parseInt)) // [ 1, 2, 3 ] <- good output again!

我的问题是:

  1. 这个unary 函数是如何工作的?
  2. 在任何实际场景中,此功能(或至少一般而言的此想法)会得到很好的使用?

任何帮助将不胜感激:)


未来读者注意:结帐currying

【问题讨论】:

  • 由于目前尚不清楚unary是指用于表示数值parseInt结果的一元数字系统还是回调函数的arity,我建议不要在实践中使用它。跨度>
  • @le_m Unary functions 是函数式编程中广泛使用的概念。 parseInt 用于此处的示例可能有点令人困惑,但我认为该名称没有任何问题。

标签: javascript higher-order-functions currying


【解决方案1】:

这个一元函数是如何工作的?

函数的length 是它在第一个具有默认值的参数之前有多少个声明 非剩余参数*。所以unary 的工作原理是查看函数是否声称只接受一个参数,如果是则返回原始函数,如果不是,则创建一个只使用一个参数调用原始函数的新函数。

这个检查不可靠,我不会使用那个版本的unary。许多函数会根据它们获得的实际参数的数量来改变它们的行为,即使它们只声明一个。

在任何实际场景中,这个功能(或者至少是这个想法)会很好用吗?

始终执行包装器的版本(不查看 length)对于像您的 parseInt 一样的情况可能很方便:您只想使用 X 参数调用另一个函数(在您的情况下为 1),无论为您的回调提供了多少。

这种事情在functional programming中相当常见。如果您一般都在进行函数式编程,那么在箭头函数的这些日子里,它可能没有多大用处。但是相当多的人以函数式方式使用 JavaScript。


* 是的,就是这么复杂。示例(需要支持 ES2015 [aka "ES6"] 默认参数值和其余参数的浏览器):

function a(one) { }
function b(one, two) { }
function c(one, two, ...rest) {}
function d(one, two = 42, ...rest) {}
function e(one, two = 42, three) {}
console.log(a.length); // 1
console.log(b.length); // 2
console.log(c.length); // 2
console.log(d.length); // 1
console.log(e.length); // 1

【讨论】:

  • @Somjit:为了可靠地工作,unary 应该是:const unary = f =&gt; arg =&gt; f(arg);——也就是说,不要看f.length,因为它不能依赖f.length 告诉它的内容。
  • @Somjit:['1', '2', '3'].map(unary(parseInt)) 的问题在于,每次map 调用它的回调时,它都会传递三个 参数:条目、它的索引和数组。 parseInt 使用它的第二个参数(如果提供):它是它应该用于解析的基数(数字基数)。所以在第一次通话时,map 执行 parseInt('1', 0, theArray) 没关系,因为 parseInt 忽略了 0;但在第二次回调中,map 执行 parseInt('2', 1, theArray),这告诉 parseInt 使用以 1 为底的数字,这是没有意义的。
  • @Somjit: unary 通过返回一个新函数来解决这个问题,该函数只会使用它收到的 first 参数调用parseInt。所以map 调用f('1', 0, theArray)f 调用parseInt('1')map 调用f('2', 1, theArray)f 调用parseInt('2')
  • 终于明白了!非常感谢!
  • @Somjit:如果实际的问题是你不明白为什么需要unary,那么你似乎本末倒置地问如何它是如何工作的。有很多重复的 questions 可以解释问题。
【解决方案2】:

在这个一元函数中,fn.length 正在检查 parseInt 的参数长度, 如果 parseInt 只有一个参数,那么它照常返回函数。但在这种情况下, 我们在 parseInt 中有 2 个参数,即 parseInt(value ,radix) 所以我们的 fn.length === 1 失败了。 如果它有 2 个参数,那么它返回 parseInt 并带有一个仅值的参数。例如 parseInt(value) 不是 parseInt(value, radix)

【讨论】:

    【解决方案3】:

    这个一元函数是如何工作的?

    函数的length 属性返回函数具有的形式参数的数量*。 unary 函数检查函数是否有一个形式参数。如果是这样,它只返回原始函数。如果不是,则返回一个新函数,该函数接受一个参数,将其传递给f,然后返回结果。

    在任何实际场景中,这个功能(或者至少是这个想法)会很好用吗?

    如示例所示,某些函数会根据传入的参数数量来改变其行为。通常(例如当您使用.map 时),您真的只想传入一个参数,而unary() 可以帮助您。


    * 或者至少以前是这样。随着 ES6 的发布,现在变得更加复杂。见 T.J.克劳德的详细回答。

    【讨论】: