[Notes] stunned hammer ah, delicious! ES streamline the core concepts of functional programming

But have to say, very often, functional programming, is really fragrant! This paper also aims in the most concise writing, explained the core concept of functional.

JS itself supports multiple programming paradigms, comparing known and increasingly popular object-oriented programming function. Nature is not new knowledge, but rather a programming concept. These paradigms each have their own characteristics, we should learn to use mastery, rather than mutually exclusive. The right place using the right technology to do the right thing, is the better solution.

Next, look at some basic concepts of functional principles:

Principles, characteristics, concept

  • The first principle function is small, the second principle is smaller.
  • Functional programming world, there is no dependence on external environment, no state, no mutation.
  • Function of the same input, the output must be the same. This is also known as referential transparency.
  • Functional programming ideas to write declarative and abstract code, rather than imperative. Imperative is to tell the compiler "how to" focus on declarative tells the compiler "what to do."

Pure function

Pure function is the same function of the input to obtain the same output.

  • Pure function should not rely on any external variables, should not change any external variables.
  • The first benefit is easy to test pure function.
  • Pure function must have a meaningful name.
  • The core is a combination of functional programming paradigm

Higher-order functions

  • Function can be passed as a parameter, referred to as higher-order functions (referred HOC)
  • Function can be returned to other functions.
// 函数可以作为参数传递
var fn = func => typeof func === 'function' && func();
var log = () => console.log('hello HOC');
fn(log)

// 函数可以被其他函数返回
var fn2 = () => log;
var func2 = fn2();
func2()
复制代码
  • The abstract by higher-order functions
// unless函数只在断言为false的时候执行函数
const unless = (predicate, fn) => {
    if (!predicate) fn();
}
// 所以我们很方便的求出一个数组中的偶数
const isEven = e => e % 2 === 0;
const isOdd = e => !isEven(e);
[1,2,3,4,5,6].forEach(e => unless(isOdd(e), () => console.log(e)))

// 定义times函数,指定函数执行n次
const times = (times, fn) => {
     for(var i = 0; i < times; i++) fn(i)
}
times(100, e => unless(isOdd(e), () => console.log(e)))
复制代码
  • The real higher-order functions: some / every / map / filter / reduce / sort, etc.
// 定义一个sortBy函数作为通用的数组排序的参数
// 根据某个属性返回一个从小到大排序的函数,作为sort的参数
const sortBy = (property) => (a, b) => a[property] > b[property] ? 1 : a[property] === b[property] ? 0 : -1;
const arr = [{name: '小猫', age: 5}, {name: '小狗', age: 1}];
arr.sort(sortBy('age'));
console.log(arr);
复制代码

Closure

  • The power of closure that can access the external variable functions, the function can be extended so that scope.
  • Unary defined function, a function of converting the received plurality of parameters to receive only a function of the parameters
// unary函数
const unary = fn => fn.length === 1 ? fn : (arg) => fn(arg);

const arrInt = [1,2,3].map(parseInt); // [1, NaN, NaN]
const arrInt2 = [1,2,3].map(unary(parseInt)); // [1, 2, 3]
复制代码
  • memoized cache function
// 纯函数的输出只依赖输入,所以可以对其做缓存操作
// 阶乘函数只依赖输入,所以是纯函数
const factorial = n => {
    if (n === 1) return 1;
    return n * factorial(n - 1);
}

// 定义memoized缓存函数
const memoized = fn => {
    const cache = {};
    return function (arg) {
        if (cache.hasOwnProperty(arg)) return cache[arg];
        return cache[arg] = fn(arg);
    }
}
// 定义阶乘的缓存函数
const memoFactorial = memoized(factorial);

// 调用
console.time('one');
memoFactorial(1000);
console.timeEnd('one'); // one: 0.115966796875ms

console.time('two');
memoFactorial(1000);
console.timeEnd('two') // two: 0.02490234375ms
复制代码
  • zip function, for the two arrays are combined into an array
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);
    }
    return result;
}
zip([1,23,4], [2,4,5], (a, b) => a + b) // [3, 27, 9]
复制代码

Partial application and currying

  • Only receives a parameter into a membership function takes two parameters known as two-element parametric, receiving a plurality of parameters known as multivariate function. Receiving uncertain parameters referred variable argument function.
  • Currying is a function of multiple parameters to convert into a nested function monohydric process.
// 定义柯里化函数
const curry = (fn) => {
    return function curryFunc(...arg) {
        if (arg.length < fn.length) {
            return function () {
                return curryFunc.apply(null, [...arg, ...arguments]);
            };
        }
        return fn.apply(null, arg);
    }
};
const func = (a, b) => console.log(a - b);
curry(func)(1)(2)
复制代码
  • Partial application that allows application developers to function portion
// 定义偏应用
// 只当partial时后续参数为udefined时才使用对应的实参替换
const partial = (fn, ...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')) // 3s后输出hello
// bug原因在于undefined已经被替换掉了,后面再调用时发现没有undefined便不会再替换
timer3s(() => console.log('hello2')) // 依旧输出hello,而不是hello2
timer3s(() => console.log('hello3'))
复制代码

And a combination of pipeline

  • Unix philosophy, a program to do just one thing, if you want to complete a new task, to rebuild better than to add a new property in the old program. (Understood as a single principle, if you want to complete the new task, re-combining multiple smaller features than a transformation of the original program is better)
  • The concept of combination, a function of the output as input to another function, passed on from right to left, the process is the combination.
// 定义组合函数
const compose = (...fns) => (val) => fns.reverse().reduce((acc, fn) => fn(acc), val);
 
// 定义一系列小的函数   
const splitStr = str => str.split(' ');
const getArrLen = arr => arr.length;
// 组合并输出
const getWords = compose(getArrLen, splitStr);
getWords('I am LengChui!') // 3
复制代码
  • Combination, each receive only a function of a parameter. If you need more reality in a parametric function, you can use curry and partil.
  • Pipes, and combinations of the same functions, but from left to right. Just a personal preference.
// 定义管道函数
const pipe = (...fns) => val => fns.reduce((acc, fn) => fn(acc), val);
// 可以达到和compose同样的输出
const getWords2 = pipe(splitStr, getArrLen);
getWords2('I am LengChui!')
复制代码
  • Positioning the pipe and the combined function of an error
// 定义identity函数,将接收到的参数打印输出
const identity = arg => {
    console.log(arg);
    return arg;
}
// 在需要的地方直接插入即可
const getWords2 = pipe(splitStr, identity, getArrLen);
复制代码

Functor

  • Functor is an ordinary object that implements the map function to generate a new object in the traversal of each object value. In short, it is a functor value of container holdings.
// 函子其实就是一个持有值的容器
const Containter = function (value) {
    this.value = value;
}
// of静态方法用来生成Container实例,省略new而已
Containter.of = function (value) {
    return new Containter(value)
}
Containter.prototype.map = function (fn) {
    return Containter.of(fn(this.value));
}

// 可以简化一下(省略of)
const Containter = function (value) {
    if (!(this instanceof Containter)) return new Containter(value);
    this.value = value;
}
Containter.prototype.map = function (fn) {
    return Containter.of(fn(this.value));
}

// es6写法
class Containter {
    constructor (value) {
        this.value = value;
    }
    // 静态方法of返回类实例
    static of(value) {
        return new Containter(value);
    }
    // map函数允许Container持有的值调用任何函数
    map(fn) {
        return Containter.of(fn(this.value));
    }
}
console.log(Containter.of(123).map(e => 2 * e)
    .map(e => e + 1).value
) // 247
复制代码
  • Maybe functor, is a powerful abstraction for error handling.
// 定义Maybe函子,和普通函子的区别在于map函数
// 会对传入的值进行null和undefined检测
class Maybe {
    constructor(value) {
        this.value = value;
    }
    static of(value) {
        return new Maybe(value);
    }
    isNothing() {
        return this.value === undefined || this.value === null;
    }
    // 检测容器持有值是否为null或undefined,如果是则直接返回null
    map(fn) {
        if (this.isNothing()) return Maybe.of(null);
        return Maybe.of(fn(this.value));
    }
}
// 可以保证程序在处理值为null或undefinede的时候不至于崩溃
// 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)
复制代码

MayBe functor can easily handle all null and undefined error
but MayBe functor not know where the error comes from.

  • Either functor, can solve the problem of branch expansion
// ES6方式实现
class EitherParent {
    constructor(value) {
        this.value = value;
    }
    // 子类会继承该方法
    static of(value) {
        // new this.prototype.constructor使得返回的实例是子类
        // 这样子类调用of方法后才可以继续链式调用
        return new this.prototype.constructor(value);
    }
}
class Nothing extends EitherParent {
    constructor(...arg) {
        super(...arg)
    }
    map() {
        return this;
    }
}
class Some extends EitherParent {
    constructor(...arg) {
        super(...arg)
    }
    map(fn) {
        return new Some(fn(this.value))
    }
}

// 实例使用
function getData() {
    try {
        throw Error('error'); // 模拟出错
        return 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))
复制代码

Either MayBe and are Pointed functor

Monad functor

  • Monad is a method of functor owned chain
  • Similar MayBe functor,
class Monad {
    constructor(value) {
        this.value = value;
    }
    static of(value) {
        return new Monad(value);
    }
    isNothing() {
        return this.value === undefined || this.value === null;
    }
    // 用于扁平化MayBe函子,但是只扁平一层
    join() {
        if (this.isNothing()) return Monad.of(null);
        return this.value;
    }
    // 直接将map后的join扁平操作封装在chain方法中
    // 使得更简便的调用
    chain(fn) {
        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;
}))
复制代码

Content Reference

  • ES6 functional programming portal classic

Continuing success, further day
I was stunned hammer, a front-end enthusiasts.
Welcome criticism and exchanges.

Reproduced in: https: //juejin.im/post/5cfcf8f7e51d455a694f94e1

Guess you like

Origin blog.csdn.net/weixin_34293902/article/details/91431315