J

JavaScript 中的柯里化简介

2025-06-04

JavaScript 中的柯里化简介

柯里化是函数操作的高级技巧之一。它允许你转换函数及其操作方式。本教程将帮助你理解 JavaScript 中的柯里化是什么、它的工作原理以及如何在代码中使用它。

什么是柯里化

首先,柯里化存在于多种语言中,而不仅仅是 JavaScript。解释柯里化的含义可能有多种方式。有些简单?我们就从这个开始吧。柯里化是一个过程。它是一个将具有特定数量参数的函数转换为嵌套函数序列的过程。

序列中的每个函数都会被返回,并且每个函数只传递一个参数。只有序列中的最后一个函数会接受分布在序列中的所有参数,执行某些操作,并返回一个或多个值。这是第一次转换。

// Curried function example:
function curriedFn(a) {
  return function(b) {
    return function(c) {
      return a + b + c
    }
  }
}

// Normal function:
function fn(a, b, c) {
  return a + b + c
}
Enter fullscreen mode Exit fullscreen mode

第二个转换是关于如何调用柯里化函数。通常,你会将所有必需的参数放在一组括号内传递。但柯里化函数并非如此。使用柯里化函数时,你需要将每个参数分别放入一组括号中。

// Calling curried function declared above:
curriedFn(11)(22)(33)
// Output:
// 66

// Calling normal function:
fn(11, 22, 33)
Enter fullscreen mode Exit fullscreen mode

柯里化是如何运作的

柯里化看起来可能有些奇怪,尤其对初学者来说。让我们来看看 JavaScript 中的柯里化是如何运作的,以及它为什么有效。

关于值、参数和闭包

理解 JavaScript 中的柯里化有多难或多难,很大程度上取决于你对闭包概念的熟悉程度。柯里化正是得益于闭包。以下是两者如何协同工作的。正如你在示例中看到的,序列中的每个函数都只接受一个参数。

每次调用函数时,作为参数传递给它的值都会丢失,这听起来很有道理。但事实并非如此。该值仍然存在于被调用函数的作用域内。更重要的是,任何处于该作用域内的函数都可以访问这个作用域内的值。

只要序列执行正在进行,所有这些值都存在并且可以访问。当序列以最后一个函数及其返回值终止时,这些现有值也将消失。这也是为什么最后一个(最内层)函数可以使用所有先前见过的参数进行操作的原因。

对于序列中最后一个函数(最内层),所有这些值仍然存在。这也是它能够与它们协同工作的原因。

function curriedFn(a) {
  // Argument "a" exists here
  return function(b) {
    // Argument "a" and "b" exist here
    return function(c) {
      // Argument "a", "b" and "c" exist here
      return a + b + c
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

关于括号

因此,最内层函数可以返回所有先前见过的值,因为它们由于闭包而保持活动状态。那么那些额外的括号呢?这些括号主要有两个用途。首先,它们允许将特定的参数传递给特定的函数。

这是由柯里化函数中参数的定义顺序决定的。第二点更重要也更有趣。每个额外的括号实际上都是一个新的函数调用。这意味着,当你看到带有三对括号的柯里化函数时,你看到的是三个函数调用。

每个调用都会调用序列中的一个函数,同时还为该函数提供所需的参数。

// Create curried function:
function curriedFn(a) {
  return function(b) {
    return function(c) {
      return a + b + c
    }
  }
}

// Calling curried function:
curriedFn(11)(22)(33)

// can be visualized as:
outermostFn(11) // curriedFn(a) { ... }
middleFn(22) // function(b) { ... }
innermostFn(33) // function(c) { ... }
Enter fullscreen mode Exit fullscreen mode

这也是为什么柯里化允许你“手动”调用每个函数的原因。每次调用都会返回一个函数。你可以做的是,将每个函数调用按顺序赋值给一个变量。除了最后一个函数,每个步骤都会导致变量被赋值给一个函数。

最后一个变量将被赋值为最后一个函数的返回值。这个最后一个值就是你调用柯里化函数时,传入所有必需的参数和一对括号后得到的值。唯一的区别在于那些额外的代码行和赋值的变量。

// Create curried function:
function curriedFn(a) {
  return function(b) {
    return function(c) {
      return a + b + c
    }
  }
}

// This:
curriedFn(11)(22)(33)

// is the same as (except those extra lines):
const firstCall = curriedFn(11)
const secondCall = firstCall(22)
const lastCall = secondCall(33)

console.log(firstCall)
// Output:
// ƒ ()
// That is:
// function(b) {
//   return function(c) {
//     return a + b + c
//   }
// }

console.log(secondCall)
// Output:
// ƒ ()
// That is:
// function(c) {
//   return a + b + c
// }

console.log(lastCall)
// Output:
// 66
// That is:
// a + b + c
Enter fullscreen mode Exit fullscreen mode

无参数的柯里化函数

柯里化通常用于定义带有一些参数的函数。然而,这并不是硬性规定。你也可以创建不接受任何参数的柯里化函数。在这种情况下,你仍然需要提供正确数量的括号,只需为空即可。

// Create curried function:
function curriedFn() {
  return function() {
    return function() {
      return function() {
        return function() {
          return '??'
        }
      }
    }
  }
}

// Call curriedFn():
curriedFn()()()()()
// Output:
// '??'
Enter fullscreen mode Exit fullscreen mode

柯里化箭头函数

正如你可以柯里化常规函数一样,你也可以柯里化箭头函数。这可以帮助你减少原本需要使用的代码量。它的原理和使用方法仍然相同。由于箭头函数的性质,语法有所不同。

// Regular curried function:
function curriedFn(a) {
  return function(b) {
    return function(c) {
      return a + b + c
    }
  }
}

// Arrow function alternative:
const curriedFn = (a) => (b) => (c) => a + b + c

// Calling the curried function:
curriedFn(11)(33)(55)
// Output:
// 99
Enter fullscreen mode Exit fullscreen mode

部分应用函数

当我们在 JavaScript 中讨论柯里化时,提到一种叫做偏应用的技术也很有帮助。原因是这两者非常相似,相似到容易让人混淆。然而,有一个关键的区别可以帮助你区分它们。

这种区别在于参数的数量。当你对一个函数进行柯里化时,序列中的每个函数只接受一个参数。而部分应用则不然。在部分应用的情况下,规则是新返回的函数必须接受更少的参数。

这意味着参数可能仍然分布在多对括号中。但是,有些括号会包含不止一个参数。当你看到类似这样的情况时,你看到的是偏函数应用,而不是柯里化函数。

// Curried function example:
function myCurriedFn(x) {
  return function(y) {
    return function(z) {
      return function(w) {
        return x * y * z * w
      }
    }
  }
}

myCurriedFn(3)(6)(3)(9)
// Output:
// 486


// Partial application function example:
function myPartApplicationFn(x) {
  return function(y, z) {// Passing two arguments instead of one
    return function(w) {
      return x * y * z * w
    }
  }
}

myPartApplicationFn(3)(6, 3)(9)
// Output:
// 486
Enter fullscreen mode Exit fullscreen mode

结论:JavaScript 中的柯里化简介

柯里化的概念可能令人困惑且难以理解。这个词本身听起来就很奇怪。它的语法也不太实用。我希望本教程能够帮助您理解这个主题,帮助您理解 JavaScript 中柯里化是如何实现的、为什么实现的,以及如何使用它。

文章来源:https://dev.to/alexdevero/introduction-to-currying-in-javascript-59kh
PREV
React 中的记忆化:简单介绍
NEXT
如何在 JavaScript 中冻结对象:Object.freeze()、Object.seal() 等