Big guy, JavaScript currying, understand?

Introduction

Where does currying come from

Currying , the transliteration of Currying . Currying is a technology for implementing multi-parameter functions at the level of compilation principles.

Before talking about currying in JavaScript, let's talk about what the original currying is and where it came from.

In the coding process, what we, as coders, are essentially doing is breaking down a complex problem into multiple programmable small problems.

Currying provides a recursive degradation implementation idea for implementing multi-parameter functions - transforming a function that accepts multiple parameters into a function that accepts a single parameter (the first parameter of the original function), and returns to accept the remaining parameters and return the result. The new functions of , in some programming languages ​​(such as Haskell), support the language feature of multi-argument functions through the Currying technique.

Therefore, Currying was originally a technology at the level of compilation principles, and its purpose was to implement multi-parameter functions .

Where does currying go?

In Haskell, functions are first-class citizens, and Currying has become a language feature from the compilation principle level. At the language feature level, what is Currying ?

In the book "Mostly adequate guide", Currying is summed up like this - calling a function with only a portion of its arguments, and having it return a function to handle the rest of the arguments .

Therefore , Currying was born in response to functional programming. After Currying , everyone went to explore to discover its use and significance. Then because of these uses and meanings, people actively expanded it to other programming languages.

Implementing Currying in JavaScript

In order to achieve the characteristics described in the sentence that only part of the parameters passed to the function is called, and it returns a function that handles the remaining parameters . Let's first write a function that implements the addition add:

function add (x, y) {

  return (x + y)

}

Now we directly implement a Curryingadd function , the function name curriedAdd, according to the above definition, curriedAddthe following conditions need to be met:

curriedAdd(1)(3) === 4

// true

var increment = curriedAdd(1)

increment(2) === 3

// true

var addTen = curriedAdd(10)

addTen(2) === 12

// true

curriedAddA function that satisfies the above conditions can be implemented with the following code snippet:

function curriedAdd (x) {

  return function(y) {

    return x + y

  }
}

Of course the above implementation has some problems: it's not generic, and we don't want to implement currying by recoding the function itself .

But curriedAddthe implementation of this shows one of the basics of implementing Currying - the lazy evaluation feature of Currying requires the use of scopes in JavaScript - to put it more informally, we need to use scopes to save the last parameter passed in .

curriedAddAbstracting may result in the following functions currying:


function currying (fn, ...args1) {

    return function (...args2) {

        return fn(...args1, ...args2)

    }
}

var increment = currying(add, 1)

increment(2) === 3

// true

var addTen = currying(add, 10)

addTen(2) === 12

// true

In this implementation, curryingthe return value of the function is actually a function that takes the remaining arguments and returns the computed value immediately. That is, its return value is not automatically Currying. So we can automatically Currying the returned function of currying by recursion .

function trueCurrying(fn, ...args) {

    if (args.length >= fn.length) {

        return fn(...args)

    }

    return function (...args2) {

        return trueCurrying(fn, ...args, ...args2)

    }
}

The above function is very short, but it already implements the core idea of ​​Currying . The core idea of ​​the curry method in Lodash, a common library in JavaScript, is not much different from the above - compare the total number of parameters accepted multiple times with the number of input parameters when the function is defined. When the number of accepted parameters is greater than or equal to the function being curried When the number of incoming parameters is , it returns the calculation result, otherwise it returns a function that continues to accept parameters.

The code snippet for implementing Currying in Lodash is longer because it takes into account more things, such as binding the this variable, etc. The code snippets in Lodash will not be posted directly here. Interested students can go and see the Lodash source code and compare the differences between the two implementations.

However , the definition and implementation of Currying are not the most important. The focus of this article is: what kind of problems can it solve in coding and development, and when faced with different problems, choose a suitable Currying, to the most appropriate solve the problem .

Currying usage scenarios

Parameter reuse

Fixed parameters and parameter reuse is one of the main uses of Currying .

The above is an example incrementof addTenparameter reuse. After fixing the first parameter of the addmethod to 10, changing the method becomes a method that adds 10 to the accepted variable value.

delayed execution

Deferred execution is also an important use case for Currying , and bind and arrow functions can also achieve the same function.

In front-end development, a common scenario is to bind the onClick event to the label, and consider passing parameters for the bound method.

Here are a few common methods to compare the pros and cons:

  1. via the data attribute

    <div data-name="name" onClick={handleOnClick} />
    

    In essence, only string data can be passed through the data attribute. If complex objects need to JSON.stringify(data)be data that satisfies the JSON object format can be passed through , but more complex objects cannot be supported. (Although most of the time there is also no need to pass complex objects)

  2. through the bind method

    <div onClick={handleOnClick.bind(null, data)} />
    

    The bind method and the curryingmethod above are very similar in function and almost the same in implementation. The only difference may be that the bind method needs to be bound to the context, that is, the first parameter of bind will be used as the this point of the original function when it runs. curryingwithout this parameter. So using curryingor bind is just a trade-off.

  3. arrow function

    <div onClick={() => handleOnClick(data))} />
    

    Arrow functions can implement deferred execution, and unlike bind methods, you must specify a context. Perhaps the only concern is that in react, some people object to writing arrow functions in jsx tags, which can easily lead to writing business logic directly in jsx tags.

  4. by currying

    <div onClick={currying(handleOnClick, data)} />
    

Performance comparison

By jsPerftesting the performance of the four methods, the results are: 箭头函数> bind> currying> trueCurrying.

Compared with the bind function, the currying function is similar in principle, but has a huge difference in performance. The reason is that the bind function is implemented by the browser, and the operating efficiency is increased.

From this result , the performance of Currying is undoubtedly the worst, but on the other hand, even trueCurryingthe implementation can reach 50w Ops/s on my personal computer, indicating that these performances do not need to be concerned.

trueCurryingThe automatic currying implemented in the method is not available in the other three methods.

Do you need Currying?

Why Currying is Needed

  1. For multi-argument function reusability

    What makes Currying stand out is that it feels like functions can be reused in this way.

    Convert addfunctions .

    For complex implementations of Currying , using Lodash as a column, provides placeholderthe magic operation of . Play tricks on the reuse of multi-parameter functions.

    import _ from 'loadsh'
    
    function abc (a, b, c) {
      return [a, b, c];
    }
    
    var curried = _.curry(abc)
    
    // Curried with placeholders.
    curried(1)(_, 3)(2)
    // => [1, 2, 3]
    
  2. Born for functional programming

    Currying is for the functional. There's a whole bunch of functional programming stuff going on, 纯函数, , compose, containerand so on. (Read the "mostly-adequate-guide" )

    Currying is essential if you want to write Pointfree Javascript style code.

    To use compose, to use things like containers, we also need Currying.

Why you don't need Currying

  1. Some features of Currying have other solutions

    If we just want to bind parameters ahead of time, we have several options out of the box, bind, arrow functions, etc., and perform better than Curring.

  2. Currying is stuck in functional programming

    In this article, we provide trueCurryingan implementation of , which is also the most in line with the definition of Currying , and also provides "novelty" features that bind, arrow functions, etc. do not have - sustainable Currying (this word is made by myself).

    But the application of this "novelty" feature is not as widespread as imagined.

    The reason for this is that Currying is a product of functional programming, it was born from functional programming, and it serves functional programming.

    JavaScript is not a true functional programming language. Compared with functional programming languages ​​such as Haskell, JavaScript uses functional features such as currying , which has additional performance overhead and lacks type deduction.

    As a result, there are fewer projects that write JavaScript code in line with functional programming ideas and specifications, which also limits the common use of technologies such as currying in JavaScript code.

    If we are not ready to write functional programming specification code and only need to bind parameters once in advance in the JSX code, then bind or arrow functions are sufficient.

in conclusion

  1. Currying is "low performance" in JavaScript, but these performances are negligible in most scenarios.
  2. The idea of ​​currying greatly helps to improve the reusability of functions.
  3. Currying was born of functional programming and stuck with functional programming. If you're not ready to write pure functional code, there are better alternatives to Currying.
  4. Functional programming, and its ideas, is something to watch, learn, and apply. So at the end of the article, Amway JavaScript programmers read this book again - "mostly-adequate-guide"

Reference link

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326095482&siteId=291194637