[toc]

Preface: Why should study this method

I came across this method several times recently in my reading of Redux, Koa principles, etc., so I took a closer look at the compose implementation in order to better understand the framework principles.

Then I also found that this belongs to functional programming, and that functional programming is the only way to advance the front end of the attack, because the concept of pure function was displayed incisively and vividly in reducer of Redux, and the idea of preserving function calculation results was also seen in many other frameworks, such as VUE and React.

So the suggestion has the time to be able to see the function trial programming.

Now, let’s learn about the compose function.

Compose profile

Compose is a group of tasks (functions) that compose performs, such as the following task queue

let tasks = [step1, step2, step3, step4]
Copy the code

Each step is a compose and is executed step by step to the end

Compose is an important tool function in functional programming, and the implementation of compose here has three points

  • The first function is multivariate (takes multiple arguments), and the following functions are unitary (takes one argument)
  • Executed from right to left
  • All functions are executed synchronously

As an example, let’s say we have the following functions

let init = (. args) = > args.reduce((ele1, ele2) = > ele1 + ele2, 0)
let step2 = (val) = > val + 2
let step3 = (val) = > val + 3
let step4 = (val) = > val + 4
Copy the code

These functions form a task queue

steps = [step4, step3, step2, init]
Copy the code

Compose the queue using compose and execute

letcomposeFunc = compose(... steps)console.log(composeFunc(1.2.3))
Copy the code

Implementation process

6 -> 6 + 2 = 8 -> 8 + 3 = 11 -> 11 + 4 = 15
Copy the code

So the process goes from init right to left, the next task takes the return of the previous task, and the tasks are synchronized, ensuring that the tasks are executed in an orderly direction and at an orderly time.

The realization of the composer

Now that we know what compose is, let’s implement it.

The easiest to understand implementation

The idea is to use the recursive process idea, constantly detect whether there is a task in the queue, if there is a task to execute, and pass the execution result later, here is a local thinking, can not predict when the task will end. It’s the easiest thing to understand intuitively.

const compose = function(. funcs) {
  let length = funcs.length
  let count = length - 1
  let result
  return function f1 (. arg1) {
    result = funcs[count].apply(this, arg1)
    if (count <= 0) {
      count = length - 1
      return result
    }
    count--
    return f1.call(null, result)
  }
}
Copy the code

To simplify, remove the args1 argument

const compose = function(. funcs) {
  let length = funcs.length
  let count = length - 1
  let result
  return function f1 () {
    result = funcs[count]()
    if (count <= 0) {
      count = length - 1
      return result
    }
    count--
    return f1(result)
  }
}
Copy the code

That’s a lot better. Let’s say we have three methods, aa, BB, cc

 function aa() {
    console.log(11);
}

function bb() {
    console.log(22);
}
function cc() {
    console.log(33);
    return 33
}

Copy the code

And then the incoming compose

compose(aa,bb,cc)
Copy the code

If count = 2, then I’m actually going to do cc

result = funcs[count]()
Copy the code

Then count. If we recursively execute f1, then we’re going to execute bb

result = funcs[count]()
Copy the code

This implements methods called from right to left from the Funcs array, passing the return value to the next one.

The following steps are the same.

This is actually a process-oriented thinking

The reduce method in handwritten javascript

Why handwritten? In fact, if you are skilled in using Reduce, I don’t think there is any need to write Reduce by hand, but I think being familiar with the internal implementation of Reduce can better understand the following content, and it is not too difficult!

 function reduce(arr, cb, initialValue){
        var num = initValue == undefined? num = arr[0]: initValue;
        var i = initValue == undefined? 1: 0
        for (i; i< arr.length; i++){
            num = cb(num,arr[i],i)
        }' return num }Copy the code

If so, the loop will start directly from I = 0, otherwise it will start from I =1.

If no initial value is passed in, num takes the first element of the array. And that’s why if you pass in the initial value, I =1, because the first one is taken out, you can’t take it again.

Let’s use the reduce method we wrote

Function fn(result, currentValue, index){return result + currentValue} var arr = [2,3,4,5] var b = reduce(arr, fn,10) var c = reduce(arr, fn) console.log(b) // 24Copy the code

Okay, now that we know the reduce principle, let’s look at the compose implementation in redux below

The implementation of compose in REdux

function compose(. funcs) {
    if (funcs.length === 0) {
        return arg= > arg
    }

    if (funcs.length === 1) {
        return funcs[0]}debugger
    return funcs.reduce((a, b) = > (. args) = >a(b(... args))) }Copy the code

Very brief, very clever, but not very hard to understand. But that’s okay.

Again, by example.

function aa() {
    console.log(11);
}

function bb() {
    console.log(22);
}
function cc() {
    console.log(33);
}
Copy the code

Given that there are only three methods, how can we execute cc, bb, and aa? Yeah, you can just write it

aa(bb(cc()))
Copy the code

That’s it, very cleverly, not only completing the order of execution, but also passing the result returned by the previous method execution to the next method to be executed.

All this code does is convert the funcs array [aa,bb,cc] to aa(bb(cc()))

funcs.reduce((a, b) = > (. args) = >a(b(... args)))Copy the code

How did you do that?

Take a look at the explanation below:

The result returned from the first execution inside Reduce is a method

(... args) => aa(bb(... args))Copy the code

So let’s simplify this method to dd, which is

dd = (. args) = >aa(bb(... args))Copy the code

When the reduce is executed internally for the second time, a is the DD method returned last time, and B is CC

So the result of the execution is

(... args) => dd(cc(... args))Copy the code

While the dd (cc (… Args)) cc before DD. Dd is bb followed by AA.

Oh my God! If it isn’t a Russian nesting doll! That’s right, for compose in Redux, the implementation principle is wahaha!

Refer to the article

Segmentfault.com/a/119000001…

The last

This article was first published on the public account “Front-end Sunshine”, welcome to join the technical exchange group.