However, I have to say that a lot of times, functional programming, is really sweet! This article also aims to elaborate the core concept of function with the most brief writing style.
JS itself supports multi-paradigm programming, the more familiar object oriented programming and the increasingly popular functional programming. It’s not a new idea, it’s a programming idea. Each of these paradigms has its own characteristics, and we should learn to use them together, rather than mutually exclusive. Using the right technology to do the right thing in the right place is a better solution.
Next, let’s look at some basic concepts of functions:
Principles, characteristics, concepts
- The first rule of a function is small, and the second rule is smaller.
- In the world of functional programming, there are no environmental dependencies, no states, no mutations.
- For the same input to a function, you must get the same output. This is also known as reference transparency.
- Functional programming advocates writing declarative and abstract code, not imperative. Imperative tells the compiler “how to do”, declarative focuses on telling the compiler “what to do”.
Pure functions
A pure function is a function that takes the same input and gives the same output.
- Pure functions should not depend on any external variables, nor should they change any external variables.
- The first benefit of pure functions is that they are easy to test.
- A pure function must have a meaningful name.
- Composition is at the heart of the functional programming paradigm
Higher-order functions
- Functions can be passed as arguments, called higher-order functions (HOC for short)
- Functions can be returned by other functions.
Var fn = func => typeof func ==='function' && func();
var log = () => console.log('hello HOC');
fn(logVar fn2 = () =>log;
var func2 = fn2();
func2()
Copy the code
- Abstraction is achieved through higher-order functions
// Unless only asserts asfalseConst unless = (predicate, fn) => {if(! predicate) fn(); } const isEven = e => e % 2 === 0; const isOdd = e => ! isEven(e); [6]. ForEach (e = > unless (isOdd (e), () = > console. The log (e))) / / definitiontimesFunction that specifies that the function executes const n timestimes = (times, fn) => {
for(var i = 0; i < times; i++) fn(i)
}
times(100, e => unless(isOdd(e), () => console.log(e)))
Copy the code
- Real higher-order functions: some every/map/filter/reduce/sort, etc
// Define a sortBy function as an argument for general array sorting // return a function that sorts from small to large based on a property, Const sortBy = (property) => (a, b) => a[property] > b[property]? 1 : a[property] === b[property] ? 0:1; const arr = [{name:'the cat', age: 5}, {name: 'dog', age: 1}];
arr.sort(sortBy('age'));
console.log(arr);
Copy the code
closure
- The power of closures is that their external function variables can be accessed, allowing the scope of the function to extend.
- Define the unary function that converts a function that takes multiple arguments to one that takes only one
Const unary = fn => fn.length === 1? fn : (arg) => fn(arg); Const arrInt = [1, 2, 3]. The map (parseInt); // [1, NaN, NaN] const arrInt2 = [1,2,3]. Map (unary(parseInt)); / / [1, 2, 3]Copy the code
- Memoized cache function
Factorial = n => {const factorial = n => {const factorial = n => {if (n === 1) return 1;
returnn * factorial(n - 1); Const memoized = fn => {const cache = {};return function (arg) {
if (cache.hasOwnProperty(arg)) return cache[arg];
returncache[arg] = fn(arg); Const memoFactorial = memoized(factorial); / / call the console. Time ('one');
memoFactorial(1000);
console.timeEnd('one'); / / one: 0.115966796875 ms console. Time ('two');
memoFactorial(1000);
console.timeEnd('two'/ / two: 0.02490234375 msCopy the code
- Zip function to combine two arrays into one
const zip = (arrLeft, arrRight, fn) => {
let result = [];
let index = 0;
let maxLength = Math.max(arrLeft.length, arrRight.length);
for (; index < maxLength; index++) {
const res = fn(arrLeft[index], arrRight[index]);
result.push(res);
}
returnresult; ,23,4} zip ([1], [2, four, five], (a, b) = > a + b) / / [9] 3, 27,Copy the code
Currie and partial applications
- Those that receive only one parameter are called unary functions, those that receive two parameters are called bivariate functions, and those that receive more than one parameter are called multivariate functions. Those that receive uncertain parameters are called variational functions.
- Currying is the process of converting a multi-argument function into a nested unary function.
// Define const curry = (fn) => {return functioncurryFunc(... arg) {if (arg.length < fn.length) {
return function () {
return curryFunc.apply(null, [...arg, ...arguments]);
};
}
returnfn.apply(null, arg); }}; const func = (a, b) => console.log(a - b); curry(func)(1)(2)Copy the code
- Partial application, allowing developers to part of the application function
// Replace const partial = (fn,...) with the corresponding argument only when partial is defined. args) => {return function(... last) {let i = 0;
const argsLen = args.length;
const lastLen = last.length;
for (; i < argsLen && i < lastLen; i++) {
args[i] === undefined && (args[i] = last[i]);
}
return fn.apply(null, args);
}
}
const timer3s = partial(setTimeout, undefined, 3000)
timer3s(() => console.log('hello'Timer3s (() => console.log())'hello2')) // Still output hello instead of hello2 timer3s(() => console.log()'hello3'))
Copy the code
Assembly and piping
- The Unix concept is that a program does only one thing well, and if you want to do a new task, it’s better to rebuild it than to add new properties to an old program. (Understood as the single principle, if you want to complete a new task, it is better to recombine several small functions than to modify an original program)
- The concept of composition, in which the output of one function is passed from right to left as the input of another, is called composition.
Const compose = (... fns) => (val) => fns.reverse().reduce((acc, fn) => fn(acc), val); // Define a series of small functions const splitStr = STR => str.split(' '); const getArrLen = arr => arr.length; Const getWords = compose(getArrLen, splitStr); getWords('I am LengChui! '/ / 3Copy the code
- In the combination, each function takes only one argument. If in reality a function takes more than one argument, curry and Partil can be used.
- Pipes, they do the same thing as combinations, but from left to right. It’s just a matter of preference.
// Define const pipe = (... fns) => val => fns.reduce((acc, fn) => fn(acc), val); Const getWords2 = pipe(splitStr, getArrLen); getWords2('I am LengChui! ')
Copy the code
- Locating errors in pipes and combinatorial functions
Const identity = arg => {console.log(arg); const identity = arg => {console.log(arg);returnarg; Const getWords2 = pipe(splitStr, identity, getArrLen);Copy the code
functor
- A functor is a plain object that implements the map function, generating a new object as it iterates over each object value. In short, a functor is a container that holds values.
// The functor is just a container that holds values, const Containter =function(value) { this.value = value; } // The static Container method is used to generate Container instances, omits new containter.of =function (value) {
return new Containter(value)
}
Containter.prototype.map = function (fn) {
returnContainter.of(fn(this.value)); } // We can simplify it (omit of) const Containter =function (value) {
if(! (this instanceof Containter))return new Containter(value);
this.value = value;
}
Containter.prototype.map = function (fn) {
returnContainter.of(fn(this.value)); } // es6 class Containter {constructor (value) {this.value = value; } // Static method of returns class instance static of(value) {returnnew Containter(value); } // The map function allows the Container to call any function map(fn) {return Containter.of(fn(this.value));
}
}
console.log(Containter.of(123).map(e => 2 * e)
.map(e => e + 1).value
) // 247
Copy the code
- The Maybe functor is a powerful abstraction for error handling.
// Class Maybe {constructor(value) {this.value = value; // this can be used as an example. } static of(value) {return new Maybe(value);
}
isNothing() {
returnthis.value === undefined || this.value === null; } // Check if the container holds null or undefined, and return null map(fn) if so.if (this.isNothing()) return Maybe.of(null);
returnMaybe.of(fn(this.value)); } // eg1 const res = maybe.of (null).map(e => null).map(e => e-10); console.log(res); // eg2 const body = {data: [{type: 1}]};
const typeAdd = e => {
e.type && e.type ++;
return e;
}
const res = Maybe.of(body).map(body => body.data)
.map(data => data.map(typeAdd))
console.log(res)
Copy the code
The MayBe functor can handle all null and undefined errors quite easily but the MayBe functor can’t know where the error is coming from.
- Either functor solves the branch extension problem
Class EitherParent {constructor(value) {this.value = value; // constructor(value) {this.value = value; } / / subclass inherits the static of the method (value) {/ / new enclosing prototype. The constructor that returns an instance of the subclass / / this subclass after the invocation of methods can only keep chain callsreturnnew this.prototype.constructor(value); } } class Nothing extends EitherParent { constructor(... arg) { super(... arg) }map() {
returnthis; } } class Some extends EitherParent { constructor(... arg) { super(... arg) } map(fn) {returnNew Some(fn(this.value))}} new Some(fn(this.value))}function getData() {
try {
throw Error('error'); // Simulation errorreturn Some.of({code: 200, data: {a: 13}})
} catch (error) {
return Nothing.of({code: 404, message: 'net error'})
}
}
console.log(getData().map(res => res.data).map(data => data.a))
Copy the code
MayBe and Either are both margin functors
Monad functor
- Monad is a functor with the chain method
- Like the MayBe functor,
class Monad {
constructor(value) {
this.value = value;
}
static of(value) {
return new Monad(value);
}
isNothing() {
returnthis.value === undefined || this.value === null; } // Flatten the MayBe functor, but only one layerjoin() {
if (this.isNothing()) return Monad.of(null);
returnthis.value; } join ();} join ();}return this.map(fn).join();
}
map(fn) {
if (this.isNothing()) return Monad.of(null);
return Monad.of(fn(this.value));
}
}
console.log(Monad.of(123).chain(e => {
e += 1;
return e;
}))
Copy the code
Content of the reference
- ES6 Functional programming Primer classic
I am leng hammer, a front end lover. Criticism and communication are welcome.