preface
I recently started working on Redux, which is based on the compose composition function. The way the compose function is implemented in REdux also takes full advantage of Reduce. The concept of a function is also somewhat obscure to me.
reduce MDN
Reduce is an array manipulation method from ES5. This method is described in MDN as (reduce method accumulates each value of the array from left to right through a given execution function, and finally produces a result).
This section describes reduce parameters
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
Copy the code
Callback is a callback function
- Accumulator Is the last accumulated value.
- CurrentValue array traverses the function being processed
- Index The corresponding index (0 if initialValue was provided, 1 otherwise)
- Array is the array of method calls
InitialValue is the initialValue of the first argument to the callback. No initial value is provided for the first element of the array as the first parameter of the callback
Implementation Principle of Reduce
The related Polyfill code is also available on MDN. To make the code work, I removed the comments and changed them slightly.
let obj = [10.11.13];
Object.defineProperty(obj, 'reduce', {
value: function (callback /*, initialValue*/) {
if (this= = =null) {
throw new TypeError('Array.prototype.reduce ' + 'called on null or undefined');
}
if (typeofcallback ! = ='function') {
throw new TypeError(callback + ' is not a function');
}
// 1. Let O be ? ToObject(this value).
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// Steps 3, 4, 5, 6, 7
var k = 0;
var value;
if (arguments.length >= 2) {
value = arguments[1];
} else {
while(k < len && ! (kin o)) {
k++;
}
if (k >= len) {
throw new TypeError('Reduce of empty array ' +
'with no initial value');
}
value = o[k++];
}
// 8. Repeat, while k < len
while (k < len) {
if (k in o) {
value = callback(value, o[k], k, o);
}
// d. Increase k by 1.
k++;
}
// 9. Return accumulator.
returnvalue; }});console.log(obj.reduce((cur, currItem) = > {
return cur + currItem
}))
Copy the code
Above, I defined an array [1,2,3] to execute the custom reduce function. The execution of each step is as follows.
– First case: the first value of the array will be used as the initial value if no initial value is passed.
Number of executions | k | value | The value passed to the callback function |
---|---|---|---|
The initial state | 0 | undefined | |
For the first time, | 1 | 10 | The callback (10, 11, 1, final three [10]) |
The second time | 2 | 21 | The callback (21, 12, 2, final three [10]) |
The third time | 3 | 34 | The callback (34, undefined, 3, final three [10]) |
- The second case passes an initial value
When you pass the initial value, you just pass the second parameter of the function as the initial value in the initial state step
if (arguments.length >= 2) {
value = arguments[1];
}
Copy the code
Reduce Advanced Applications
(1) Execute the promise function sequentially
const f1 = () = > new Promise((resolve, reject) = > {
setTimeout(() = > {
console.log('p1 running')
resolve(1)},1000)})const f2 = () = > new Promise((resolve, reject) = > {
setTimeout(() = > {
console.log('p2 running')
resolve(2)},1000)})const array = [f1, f2];
const runPromiseAarrayFun = (array, value) = > {
return array.reduce((promisefn, currentFunction) = > promisefn.then(currentFunction), Promise.resolve(value))
}
runPromiseAarrayFun(array, 'init')
Copy the code
- The output
p1 running
p2 running
Copy the code
- Note: In fact, es6 already has asynchronous function “synchronous execution scheme”. For example async await can also implement promise synchronous execution. But async await can only execute a fixed number of asynchronous functions. Using async await cannot be done when the number of asynchronous functions is uncertain.
(2) Pipe implementation principle
Pipe is a Corrified function that takes a parameter as an initial value, and works by solving the problem that g(f(m(… Arg))) nesting depth uncertainty when nesting calls.
- Example: from MDN
// Building-blocks to use for composition
const double = x= > x + x;
const triple = x= > 3 * x;
const quadruple = x= > 4 * x;
// Function composition enabling pipe functionality
const pipe = (. functions) = > input= > functions.reduce(
(acc, fn) = > {
debugger
return fn(acc)
},
input
);
const multiply24 = pipe(double, triple, quadruple);
multiply24(10); / / 240
Copy the code
When multiply24(10) is executed, the above three functions are executed in sequence for parameter 10, but the value of the next function is the result of the last one. This feature is similar to the principle of Reduce.
- Implementation process
Number of executions | value | The value passed to the next function | equivalent |
---|---|---|---|
The initial state | 10 | ||
For the first time, | 10 | double(10) | double(10) |
The second time | 20 | triple(20) | triple(double(10)) |
The third time | 60 | quadruple(60) | quadruple(triple(double(10))) |
compose
Compose is also known as a composition function. His principle is similar to pipe’s. Mainly used to perform a series of tasks of indefinite length.
- Pipe execution order
letfunarrs = [f1,f2,f3,f4]; pipe(... funarrs); Equivalent to the f4 (f3 (f2 (f1 (... args))));Copy the code
- Pipe execution order
letfunarrs = [f1,f2,f3,f4]; pipe(... funarrs); Equivalent of f1, f2, f3, f4 (... args))));Copy the code
Recursive implementation
const compose = function (. args) {
let length = args.length;
let count = length - 1;
let result
return function f1(. arg1) {
debugger
result = args[count].apply(this, arg1);
if (count <= 0) {
count = length - 1;
return result;
}
count--;
console.log('countttttttttttttttt')
return f1.call(null, result)
}
}
function f1() {
console.log("f1")
return 1;
}
function f2() {
console.log("f2")
return 2;
}
function f3() {
console.log("f3");
return 3;
}
function f4() {
console.log("f4")
return 4;
}
let funcs = [f1, f2, f3, f4];
letcomposeFunc = compose(... funcs)Copy the code
Number of executions | Does the recursion end | The count value | Executive function | arg |
---|---|---|---|---|
For the first time, | no | 3 | f4 | empty |
The second time | no | 2 | f3 | 4 |
The third time | no | 1 | f2 | 3 |
For the fourth time | is | 0 | f1 f1.apply(this, arg1) | 3 |
The closure is used here, and the count argument in the compose function is cached. The logic of each execution is to take the last return value as the parameter of this execution, and the procedure can use recursion, and the recursion ends when count==0, and the recursion breaks out. Execute the f1 function directly.
Reduce implementation
const compose = (. args) = > {
return args.reverse().reduce((f, g) = > {
return (. arg) = > {
return g.call(this, f.apply(this, arg))
}
}, args.shift())
}
function f1(x) {
return x + 1;
}
function f2(x) {
return x + 2;
}
function f3(x) {
return x + 3;
}
function f4(x) {
return x + 4;
}
let funcs = [f1, f2, f3, f4]
console.log(compose(... funcs)(0))
Copy the code
- The initial value of 0 for the first time — > the args. The shift () = = f4 – > right all (this, f.a pply (this, arg)) < = > f3. Call (this, f4. Apply (this, 0));
- F2. call(this, f3.call(this, f3.call(this, f3.call(this, f4.apply(this, 0))));
- Third time 0 – > right all (this, f.a pply (this, arg)) < = > f1. The call (this, f2. Apply (this, f2. Call (this, f3. Apply (this, f4. Apply (this, 0)))));
- Apply (this, f2.call(this, f2.call(this, f3.apply(this, f3.apply(this, f3.apply(this, f4.apply(this, 0)))));
The principle of reduce to realize compose is to use reduce to accumulate new information and take the last accumulated function value as the parameter of the current function.
The resources
- pipe
- compose