美文网首页JavaScript
函数式编程入门系列二

函数式编程入门系列二

作者: Eastboat | 来源:发表于2019-11-26 18:27 被阅读0次

    数组的函数式编程

    数组用于遍历,用数组存储、操作和查找数据,以及转换投影数据格式

    以下创建的函数可能在数组或对象的原型中,也有可能不在,我们可以通过这些函数理解其中的运行机制,而不是覆盖原生方法

    1.数组的函数式方法

    以下创建的所有函数称为投影函数,把函数应用于一个值并创建一个新值得过程称之为投影

    map

    const forEach = (array, fn) => {
      for (const value of array) {
        fn(value);
      }
    };
    //map与forEach实现类似
    const map = (array, fn) => {
      let results = [];
      for (const value of array) {
        results.push(fn(value));
      }
      return results;
    };
    
    console.log(map([1, 2, 3], x => x * x * 10)); [10,40,90]
    
    
    //常量导出
    const arrayUtils={
        map:map
    }
    export { arrayUtils };
    
    //模块使用
    import arrayUtils from 'lib';
    arrayUtils.map
    //或者
    const map=arrayUtils.map;
    
    
    let books=[
      {"name":'深入浅出vue.js',"author":"李四",id:001},
      {"name":"CSS权威指南","author":"张三",id:002},
      {"name":"Java权威指南","author":"赵五",id:003},
      {"name":"微信小程序入门","author":"王二",id:004},
    ]
    
    console.log(map(books,(item)=>({title:item.name,code:item.id})))
    
    /*
    [ { title: '深入浅出vue.js', code: 1 },
      { title: 'CSS权威指南', code: 2 },
      { title: 'Java权威指南', code: 3 },
      { title: '微信小程序入门', code: 4 } ]
    
    */
    
    

    filter函数

    //书籍相关数据
    let apressBooks = [
      {
        "id": 111,
        "title": "C# 6.0",
        "author": "ANDREW TROELSEN",
        "rating": [4.7],
        "reviews": [{good : 4 , excellent : 12}]
      },
      {
        "id": 222,
        "title": "Efficient Learning Machines",
        "author": "Rahul Khanna",
        "rating": [4.5],
        "reviews": []
      },
      {
        "id": 333,
        "title": "Pro AngularJS",
        "author": "Adam Freeman",
        "rating": [4.0],
        "reviews": []
      },
      {
        "id": 444,
        "title": "Pro ASP.NET",
        "author": "Adam Freeman",
        "rating": [4.2],
        "reviews": [{good : 14 , excellent : 12}]
      }
    ];
    //获取评分高于4.5的书籍
    const filter = (array, fn) => {
      let results = [];
      for (const value of array) {
        (fn(value)) ? results.push(value): undefined
      }
      return results;
    }
    var res = filter(apressBooks, (book) => book.rating[0] > 4.5)
    console.log(res)
    /*
    [ { id: 111,
        title: 'C# 6.0',
        author: 'ANDREW TROELSEN',
        rating: [ 4.7 ],
        reviews: [ [Object] ] } ]
    */
    

    2.连接操作

    我们希望连接投影函数map和filter,以便获取更复杂的期望结果

    //从书籍数据中获取含有title和anthor对象且评分高于4.5的对象
    //方法:结合map和filter
    const map = (array, fn) => {
      let results = [];
      for (const value of array) {
        results.push(fn(value));
      }
      return results;
    };
    const filter = (array, fn) => {
      let results = [];
      for (const value of array) {
        (fn(value)) ? results.push(value): undefined
      }
      return results;
    }                                                                                  
    let data1 = filter(apressBooks, item => item.rating[0] > 4.5);
    let data2 = map(data1, book => ({
      title: book.title,
      author: book.author
    }))
    console.log(data2) //[ { title: 'C# 6.0', author: 'ANDREW TROELSEN' } ]
    

    mapfilter都是投影函数,他们对数组应用转换操作(通过传入高阶函数)后再返回新的数据,所以我们可以连接两个函数完成上面的任务

    任务:map基于评分高于4.5的数据,返回了带有title,author字段的对象

    //此时需要注意map和filter的顺序
    map(filter(apressBooks, item => item.rating[0] > 4.5), book => ({
      title: book.title,
      author: book.author
    }))
    

    concatAll函数

    //concatAll函数
    let apressBooks2 = [{
            name: "beginners",
            bookDetails: [{
                    "id": 111,
                    "title": "C# 6.0",
                    "author": "ANDREW TROELSEN",
                    "rating": [4.7],
                    "reviews": [{
                        good: 4,
                        excellent: 12
                    }]
                },
                {
                    "id": 222,
                    "title": "Efficient Learning Machines",
                    "author": "Rahul Khanna",
                    "rating": [4.5],
                    "reviews": []
                }
            ]
        },
        {
            name: "pro",
            bookDetails: [{
                    "id": 333,
                    "title": "Pro AngularJS",
                    "author": "Adam Freeman",
                    "rating": [4.0],
                    "reviews": []
                },
                {
                    "id": 444,
                    "title": "Pro ASP.NET",
                    "author": "Adam Freeman",
                    "rating": [4.2],
                    "reviews": [{
                        good: 14,
                        excellent: 12
                    }]
                }
            ]
        }
    ];
    const map = (array, fn) => {
        let tempArr = [];
        for (let value of array) {
            tempArr.push(fn(value))
        }
        return tempArr;
    }
    console.log(map(apressBooks2, (book) => book.bookDetails))
    /*
    返回数据包含了数组中的数组,如果继续使用filter,将无法过滤,因为filter不能再嵌套数组中运行
    [ [ { id: 111,
          title: 'C# 6.0',
          author: 'ANDREW TROELSEN',
          rating: [Array],
          reviews: [Array] },
        { id: 222,
          title: 'Efficient Learning Machines',
          author: 'Rahul Khanna',
          rating: [Array],
          reviews: [] } ],
      [ { id: 333,
          title: 'Pro AngularJS',
          author: 'Adam Freeman',
          rating: [Array],
          reviews: [] },
        { id: 444,
          title: 'Pro ASP.NET',
          author: 'Adam Freeman',
          rating: [Array],
          reviews: [Array] } ] ]
    */
    
    //主要作用是将嵌套数组转换为非嵌套的单一数组(降维操作)
    const concatAll = (array, fn) => {
        let results = [];
        for (const value of array) {
            results.push.apply(results, value)
            //遍历时把内部二层数组通过push保存到结果数组中
        }
        return results;
    }
    console.log(
        concatAll(map(apressBooks2, (book) => book.bookDetails))
    )
    /*
    [ { id: 111,
        title: 'C# 6.0',
        author: 'ANDREW TROELSEN',
        rating: [ 4.7 ],
        reviews: [ [Object] ] },
      { id: 222,
        title: 'Efficient Learning Machines',
        author: 'Rahul Khanna',
        rating: [ 4.5 ],
        reviews: [] },
      { id: 333,
        title: 'Pro AngularJS',
        author: 'Adam Freeman',
        rating: [ 4 ],
        reviews: [] },
      { id: 444,
        title: 'Pro ASP.NET',
        author: 'Adam Freeman',
        rating: [ 4.2 ],
        reviews: [ [Object] ] } ]
    */
    
    const filter = (array, fn) => {
        let results = [];
        for (const value of array) {
            (fn(value)) ? results.push(value): undefined
        }
        return results;
    }
    console.log(
        filter(
            concatAll(map(apressBooks2, (book) => book.bookDetails)),
            (book)=>book.rating[0]>4.5
        )
    )
    
    /*
    [ { id: 111,
        title: 'C# 6.0',
        author: 'ANDREW TROELSEN',
        rating: [ 4.7 ],
        reviews: [ [Object] ] } ]
    */
    
    

    说明:
    results.push.apply(results, value)
    Function.prototype.call 和 Function.prototype.apply 都是非常常用的方法
    apply 接受两个参数,第一个参数指定了函数体内 this 对象的指向,第二个参数为一个带下标的集合, 这个集合可以为数组,也可以为类数组 , apply 方法把这个集合中的元素作为参数传递给被调用的函数。

    var fn=function(a,b){console.log(a,b)};
    fn(1,2)
    fn.call(this,1,2);
    fn.apply(this,[1,3])
    

    3.reduce函数

    //数组求和
    let arr=[2,3,4,5,1];
    const forEach = (array, fn) => {
        let i;
        for (i = 0; i < array.length; i++) {
          fn(array[i]);
        }
      };
    let temp=0; //累加器开始
    forEach(arr,(item)=>temp+=item);
    console.log(temp) //15
    

    思考:如果计算数组的乘积,我们就要把temp设置为1,是不是很麻烦?

    这种将数组每一项遍历生成最后一个单一结果的过程,我们称之为归约数组
    reduce函数改写

    //reduce函数写法
    const reduce=(array,fn)=>{
        let accumlator=0;
        for(const value of array){
            accumlator=fn(accumlator,value);
        }
        return [accumlator]
    }
    console.log(reduce(arr,(acc,val)=>acc*val)) //0 原因是累加器的初始值为0
    
    
    
    //考虑初始累加器值, 进一步改写
    const reduce = (array, fn, initialValue) => {
        let accumlator;
        if (initialValue != undefined) {
            accumlator = initialValue
        } else {
            accumlator = array[0]
        }
        if (initialValue === undefined) {
            for (let i = 1; i < array.length; i++) {
                accumlator = fn(accumlator, array[i])
            }
        } else {
            for (const value of array) {
                accumlator = fn(accumlator, value)
            }
        }
        return [accumlator]
    }
    console.log(reduce([1, 2, 3, 4], (acc, val) => acc * val, 1)) //[24]
    console.log(reduce([1, 2, 3, 4], (acc, val) => acc * val)) //[24]
    
    

    案例

    
    let apressBooks3 = [{
            name: "beginners",
            bookDetails: [{
                    "id": 111,
                    "title": "C# 6.0",
                    "author": "ANDREW TROELSEN",
                    "rating": [4.7],
                    "reviews": [{
                        good: 4,
                        excellent: 12
                    }]
                },
                {
                    "id": 222,
                    "title": "Efficient Learning Machines",
                    "author": "Rahul Khanna",
                    "rating": [4.5],
                    "reviews": []
                }
            ]
        },
        {
            name: "pro",
            bookDetails: [{
                    "id": 333,
                    "title": "Pro AngularJS",
                    "author": "Adam Freeman",
                    "rating": [4.0],
                    "reviews": []
                },
                {
                    "id": 444,
                    "title": "Pro ASP.NET",
                    "author": "Adam Freeman",
                    "rating": [4.2],
                    "reviews": [{
                        good: 14,
                        excellent: 12
                    }]
                }
            ]
        }
    ];
    
    //统计评价为good和excellent的数量
    const map = (array, fn) => {
        let tempArr = [];
        for (let value of array) {
            tempArr.push(fn(value))
        }
        return tempArr;
    }
    //主要作用是将嵌套数组转换为非嵌套的单一数组
    const concatAll = (array, fn) => {
        let results = [];
        for (const value of array) {
            results.push.apply(results, value)
            //遍历时把内部二层数组通过push保存到结果数组中
        }
        return results;
    }
    //获取内部属性中的数组,然后合并成在一个层级
    let bookDetails = concatAll(
        map(apressBooks3, item => item.bookDetails)
    )
    let res = reduce(bookDetails, (acc, bookDetails) => {
        let goodReviews = bookDetails.reviews[0] != undefined ? bookDetails.reviews[0].good : 0;
        let excellentReviews = bookDetails.reviews[0] != undefined ? bookDetails.reviews[0].excellent : 0;
        return {
            good: acc.good + goodReviews,
            excellent: acc.excellent + excellentReviews
        }
    }, { 
        good: 0,
        excellent: 0
        //累加器初始值
    })
    
    console.log(res) //[ { good: 18, excellent: 24 } ]
    

    4.zip函数

    我们实际的数据是后台给我们的,假如后台给我们的数据都是分片段的,我们如何结合这些数据来使用?

    // 各类书籍的reviews被分割到了另一个数组中,怎么处理这些分割的数据?
    let apressBooks = [{
            name: "beginners",
            bookDetails: [{
                    "id": 111,
                    "title": "C# 6.0",
                    "author": "ANDREW TROELSEN",
                    "rating": [4.7]
                },
                {
                    "id": 222,
                    "title": "Efficient Learning Machines",
                    "author": "Rahul Khanna",
                    "rating": [4.5],
                    "reviews": []
                }
            ]
        },
        {
            name: "pro",
            bookDetails: [{
                    "id": 333,
                    "title": "Pro AngularJS",
                    "author": "Adam Freeman",
                    "rating": [4.0],
                    "reviews": []
                },
                {
                    "id": 444,
                    "title": "Pro ASP.NET",
                    "author": "Adam Freeman",
                    "rating": [4.2]
                }
            ]
        }
    ];
    
    let reviewDetails = [{
            "id": 111,
            "reviews": [{
                good: 4,
                excellent: 12
            }]
        },
        {
            "id": 222,
            "reviews": []
        },
        {
            "id": 333,
            "reviews": []
        },
        {
            "id": 444,
            "reviews": [{
                good: 14,
                excellent: 12
            }]
        }
    ]
    
    
    
    /**
     *
     *
     * @description 根据fn获取array内容
     * @param {*} array
     * @param {*} fn
     * @returns
     */
    const map = (array, fn) => {
        let tempArr = [];
        for (let value of array) {
            tempArr.push(fn(value))
        }
        return tempArr;
    }
    
    
    /**
     *
     * @description 将嵌套数组转换为非嵌套的单一数组
     * @param {*} array
     * @param {*} fn
     * @returns 
     * 
     */
    const concatAll = (array, fn) => {
        let results = [];
        for (const value of array) {
            results.push.apply(results, value)
            //遍历时把内部二层数组通过push保存到结果数组中
        }
        return results;
    }
    
    /**
     *
     *
     * @description 合并两个给定的数组
     * @param {*} leftArr 
     * @param {*} rightArr
     * @param {*} fn
     */
    const zip = (leftArr, rightArr, fn) => {
        let index, results = [];
        for (index = 0; index < Math.min(leftArr.length, rightArr.length); index++) {
            results.push(fn(leftArr[index], rightArr[index]));
    
        }
        return results
    }
    
    //假设将a,b数组内容相加
    var a=[1,2,3];
    var b=[2,3,5,7];
    console.log(zip(a,b,(x,y)=>x+y));  //[3,5,8]
    
    
    //统计书籍评价为good和excellent总数, 前提需要合并这两个不同数组结构
    let bookDetails=concatAll(map(apressBooks,item=>item.bookDetails))
    let resData=zip(bookDetails,reviewDetails,(book,review)=>{
        if(book.id===review.id){
            //改变默认的book对象将影响bookDetails的内容
            //所以一旦创建clone对象我们就添加一个description属性
            let clone=Object.assign({},book);
            clone.description=review
            return clone;
        }
    })sddsds
    console.log(resData);
    /*
    [ { id: 111,
        title: 'C# 6.0',
        author: 'ANDREW TROELSEN',
        rating: [ 4.7 ],
        description: { id: 111, reviews: [Array] } },
      { id: 222,
        title: 'Efficient Learning Machines',
        author: 'Rahul Khanna',
        rating: [ 4.5 ],
        reviews: [],
        description: { id: 222, reviews: [] } },
      { id: 333,
        title: 'Pro AngularJS',
        author: 'Adam Freeman',
        rating: [ 4 ],
        reviews: [],
        description: { id: 333, reviews: [] } },
      { id: 444,
        title: 'Pro ASP.NET',
        author: 'Adam Freeman',
        rating: [ 4.2 ],
        description: { id: 444, reviews: [Array] } } ]
    
    */
    
    
    

    柯里化与偏应用

    1.一些术语

    1.1 一元函数

    const identity=(x)=>x;
    

    1.2 二元函数

    const add=(x,y)=>x+y;
    

    1.3 变参参数

    function print(a){
        console.log(a);
        console.log(arguments) 
    }
    
    print(1,2,3)
    // 1
    //{ '0': 1, '1': 2, '2': 3 }
    
    //采用es6语法的扩展运算符捕获参数
    const variadic=(a,...variadic)=>{
        console.log(a);
        console.log(variadic)
    }
    variadic(1,2,3)
    //1
    //[2,3]
    

    2.柯里化(Curry)

    柯里化是指把一个多参数函数转换为一个嵌套的一元函数的过程

    //二元函数
    const add=(x,y)=>x+y
    add(1,1) //2
    
    //add函数柯里化后
    // function addCurried(x){
    //     return function(y){
    //         return x+y;
    //     }
    // }
    const addCurried=x=>y=x+y;
    addCurried(4)(4) //8
    

    2.1 curry函数

    const add=(x,y)=>x+y
    
    //只能处理二元函数
    const curry = (binaryFn) => {
        return function (firstArg) {
            return function (secondArg) {
                return binaryFn(firstArg, secondArg)
            }
        }
    }
    let autoCurriedAdd=curry(add);
    console.log(autoCurriedAdd(2)(2));
    

    2.2 柯里化用例:

    //创建table2,table3,table3
    const table2=(y)=>2*y;
    const table3=(y)=>3*y;
    const table3=(y)=>4*y;
    
    //统一方法并使用
    const genericTable=(x,y)=>x*y;
    table2_item1=genericTable(2,1)
    table2_item2=genericTable(2,2)
    table2_imte3=genericTable(2,3)
    
    //curry化表格函数
    
    //curry化表格函数
    const table2=curry(genericTable)(2);
    const table3=curry(genericTable)(3);
    const table4=curry(genericTable)(4);
    
    console.log('table is currying');
    console.log(table2(2)) // 2*2
    console.log(table3(2)) // 3*2
    console.log(table4(2)) // 4*2
    

    2.3 日志函数案例

    开发者编写代码的时候会在应用的不同阶段编写很多日志

    const loggerHelper=(mode,initialMessage,errorMessage,lineNo)=>{
        if(mode==='DEBUG'){
            console.debug(initialMessage,errorMessage+"at line"+lineNo)
        }else if(mode==='ERROR'){
            console.error(initialMessage,errorMessage+"at line"+lineNo)
        }else if(mode==='WARN'){
            console.warn(initialMessage,errorMessage+"at line"+lineNo)
        }else{
            throw 'Wrong mode'
        }
    }
    
    loggerHelper('ERROR','Error At Stats.js',"Invalid argument passed",23)
    loggerHelper('ERROR','Error At Stats.js',"slice is not undefined",31)
    //此时调用我们重复使用了参数 mdoe和initialMessage,我们需要重新定义一个curry函数
    
    

    添加类型判断以及传递参数

    //添加参数类型判断
    let curryFunc = (fn) => {
        if (typeof fn != 'function') {
            throw Error('No function provided');
        } else {
            //返回一个变参函数,
            return function curriedFn(...args) {
                return fn.apply(null, args)
            }
        }
    }
    
    const multiply = (x, y, z) => x * y * z;
    console.log(curryFunc(multiply)(1, 2, 3)) //6
    
    
    //把多参数函数转换为一元函数的curry函数
    let curryFuncTpl = (fn) => {
        if (typeof fn != 'function') {
            throw Error('No function provided');
        } else {
            return function curriedFn(...args) {  //返回一个变参函数,如果参数个数小于最初的fn.length,则递归调用,继续收集参数
                if (args.length < fn.length) {
                    return function () {
                        return curriedFn.apply(null, args.concat([].slice.call(arguments))) //concat连接一次传入的一个参数
                    }
                }else{
                    return fn.apply(null,args)
                }
            }
        }
    }
    console.log(curryFuncTpl(multiply)(1)(2)(3)) //6
    
    const multiply2 = (x, y, z,m)=>x+y+z+m;
    console.log(curryFuncTpl(multiply2)(1)(2)(3)(4)) //10
    
    

    3.柯里化小案例

    1.在数组内容中查找数字,返回包含数字的数组内容

    //无需柯里化时,我们可以如下实现。
    
    let match = curryFuncTpl(function (expr, str) {
        return str.match(expr);
    });
    
    let hasNumber = match(/[0-9]+/)
    
    let filter = curryFuncTpl(function (f, ary) {
        return ary.filter(f);
    });
    
    let findNumbersInArray = filter(hasNumber)
    
    
    console.log("Finding numbers via curry", findNumbersInArray(["js", "number1",'22']))
    //Finding numbers via curry [ 'number1' ,'22']
    
    1. 求数组的平方
    
    let map = curryFuncTpl(function (f, ary) {
        return ary.map(f);
    });
    let squareAll=map((x)=>x*x)
    square([1,2,3]) //[1,4,9]
    

    4.数据流

    设计的curry函数总是在最后接受数组

    4.1 偏应用

    假设我们在每10毫秒后做一组操作,通过setTimeout函数实现

    setTimeout(()=>console.log('Do some task'),10)
    setTimeout(()=>console.log('Do some task'),10)
    
    //question:每次都传入了10,能隐藏吗?能用curry函数解决吗?
    //answer:不能,因为curry函数参数列表是从最左到最右的
    
    

    我们要根据需要传递参数,并将10保存为常量,所以不能使用curry函数,一个变通的方法就是封装setTimeout函数,这样函数参数就会变成最右边的一个

    const setTimeoutWapper=(time,fn)=>{
        setTimeout(fn,time)
    }
    // const delayTenMs=curryN(setTimeoutWapper)
    //console.log(delayTenMs(()=>console.log('Do some task')))
    console.log(curryN(setTimeoutWapper)(10)(()=>console.log('Do some task')))
    
    //缺点:我们不得不创建setTimeoutWapper这种封装器,这是一种开销
    

    4.2 实现偏函数

    偏函数允许我们部分的应用函数参数

    const partial = function (fn,...partialArgs){
      let args = partialArgs.slice(0);
      return function(...fullArguments) {
        let arg = 0;
        for (let i = 0; i < args.length && arg < fullArguments.length; i++) {
          if (args[i] === undefined) {
            args[i] = fullArguments[arg++];
            }
          }
          return fn.apply(this, args);
      };
    };
    
    //使用偏函数
    let delayTenMsPartial = partial(setTimeout,undefined,10);
    delayTenMsPartial(() => console.log("Do X. . .  task"))
    delayTenMsPartial(() => console.log("Do Y . . . . task"))
    
    

    5.组合与管道

    函数式编程其实就是组合函数,利用一些小函数来构建一个新函数

    函数式组合在函数式编程中被称为组合(composition)

    5.1 组合的概念

    函数式编程遵循了Unix的理念:
    1. 每个程序只做好一件事 ,为了完成一个新的需求,'重新构建'好于'在复杂的旧程序中添加新的属性',这正是我们创建函数时秉承的理念,我们之前讨论的函数都应该接受一个参数并返回数据.

    2. 每个程序的输出应该是另一个尚未可知的程序的输入

    操作unix平台的命令

    cat test.txt  //cat紧跟文件位置,并输出test.txt内容
    //输出 hello world
    
    cat test.txt | grep 'world'  //cat返回文本内容后再给grep执行一次搜索
    
    
    car test.txt | grep 'world' | wc  //计算单词world在给定的文本文件中的出现次数
    

    管道符|将左侧的函数输出作为输入发送给右边的函数,该处理过程就称为管道.

    随着需求的随时加入,我们通过基础函数创建了一个个新函数,也就是说组合成一个新函数
    基础函数必须遵守规则

    每一个基础函数都需要接受一个参数并返回数据

    5.2 函数式组合

    let apressBooks = [{
        "id": 111,
        "title": "C# 6.0",
        "author": "ANDREW TROELSEN",
        "rating": [4.7],
        "reviews": [{
          good: 4,
          excellent: 12
        }]
      },
      {
        "id": 222,
        "title": "Efficient Learning Machines",
        "author": "Rahul Khanna",
        "rating": [4.5],
        "reviews": []
      },
      {
        "id": 333,
        "title": "Pro AngularJS",
        "author": "Adam Freeman",
        "rating": [4.0],
        "reviews": []
      },
      {
        "id": 444,
        "title": "Pro ASP.NET",
        "author": "Adam Freeman",
        "rating": [4.2],
        "reviews": [{
          good: 14,
          excellent: 12
        }]
      }
    ];
    //获取评分高于4.5的书籍,然后获取title和author字段
    const map = (array, fn) => {
      let results = [];
      for (const value of array) {
        results.push(fn(value));
      }
      return results;
    };
    const filter = (array, fn) => {
      let results = [];
      for (const value of array) {
        (fn(value)) ? results.push(value): undefined
      }
      return results;
    }
     map(filter(apressBooks, item => item.rating[0] > 4.5), book => ({
      title: book.title,
      author: book.author
    }))
    /*
    /*
    [ { id: 111,
        title: 'C# 6.0',
        author: 'ANDREW TROELSEN',
        rating: [ 4.7 ],
        reviews: [ [Object] ] } ]
     
     先过滤后获取需要的字段
    
     [ { title: 'C# 6.0', author: 'ANDREW TROELSEN' } ]
    */
    
    

    filter输出的数据被作为输入参数传递给map函数,现在我们需要通过创建一个compose函数,把一个函数的输出作为输入发送给另一个函数

    //接受a,b两个参数,并返回一个接受参数c的函数
    //当c调用返回函数时,参数c被函数b调用,b的输出作为a的输出
    const compose=(a,b)=>(c)=>a(b(c))  //函数调用从右往左
    

    5.3 应用compose函数

    假设对一个给定的数组四舍五入求值

    // let data = parseFloat("3.56");
    // let number = Math.round(data);
    // console.log(number);
    
    //利用comppose函数改写
    
    const compose = (a, b) => c => a(b(c));
    let getNumber = compose(Math.round, parseFloat);
    // getNumber = c => Math.round(parseFloat(c));
    console.log(getNumber("3.56")); //此处调用getNumber才行
    

    假设构建一个函数计算一个字符串中单词的数量

    let splitIntoSpaces = str => str.split(" ");
    let count = array => array.length;
    const getWordsCount = compose(count, splitIntoSpaces);
    // getWordsCount =c=>count(splitIntoSpaces(c))
    console.log(getWordsCount("This is a javascript code")); //5
    
    

    相关文章

      网友评论

        本文标题:函数式编程入门系列二

        本文链接:https://www.haomeiwen.com/subject/yweywctx.html