[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.