我的编程空间,编程开发者的网络收藏夹
学习永远不晚

JS组合函数实例分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

JS组合函数实例分析

本篇内容介绍了“JS组合函数实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

组合函数

含义

函数编程就像拼乐高!

乐高有各式各样的零部件,我们将它们组装拼接,拼成一个更大的组件或模型。

函数编程也有各种功能的函数,我们将它们组装拼接,用于实现某个特定的功能。

下面来看一个例子,比如我们要使用这两个函数来分析文本字符串:

function words(str) {    return String( str )        .toLowerCase()        .split( /\s|\b/ )        .filter( function alpha(v){            return /^[\w]+$/.test( v );        } );}function unique(list) {    var uniqList = [];    for (let i = 0; i < list.length; i++) {        if (uniqList.indexOf( list[i] ) === -1 ) {            uniqList.push( list[i] );        }    }    return uniqList;}var text = "To compose two functions together";var wordsFound = words( text );var wordsUsed = unique( wordsFound );wordsUsed;//  ["to", "compose", "two", "functions", "together"]

不用细看,只用知道:我们先用 words 函数处理了 text,然后用 unique 函数处理了上一处理的结果 wordsFound;

这样的过程就好比生产线上加工商品,流水线加工。

JS组合函数实例分析

想象一下,如果你是工厂老板,还会怎样优化流程、节约成本?

这里作者给了一种解决方式:去掉传送带!

JS组合函数实例分析

即减少中间变量,我们可以这样调用:

var wordsUsed = unique( words( text ) );wordsUsed

确实,少了中间变量,更加清晰,还能再优化吗?

我们还可以进一步把整个处理流程封装到一个函数内:

function uniqueWords(str) {    return unique( words( str ) );}uniqueWords(text)

这样就像是一个黑盒,无需管里面的流程,只用知道这个盒子输入是什么!输出是什么!输入输出清晰,功能清晰,非常“干净”!如图:

JS组合函数实例分析

与此同时,它还能被搬来搬去,或再继续组装。

我们回到 uniqueWords() 函数的内部,它的数据流也是清晰的:

uniqueWords <-- unique <-- words <-- text

封装盒子

上面的封装 uniqueWords 盒子很 nice ,如果要不断的封装像 uniqueWords 的盒子,我们要一个一个的去写吗?

function uniqueWords(str) {    return unique( words( str ) );}function uniqueWords_A(str) {    return unique_A( words_A( str ) );}function uniqueWords_B(str) {    return unique_B( words_B( str ) );}...

所以,一切为了偷懒,我们可以写一个功能更加强大的函数来实现自动封装盒子:

function compose2(fn2,fn1) {    return function composed(origValue){        return fn2( fn1( origValue ) );    };}// ES6 箭头函数形式写法var compose2 =    (fn2,fn1) =>        origValue =>            fn2( fn1( origValue ) );

JS组合函数实例分析

接着,调用就变成了这样:

var uniqueWords = compose2( unique, words );var uniqueWords_A = compose2( unique_A, words_A );var uniqueWords_B = compose2( unique_B, words_B );

太清晰了!

任意组合

上面,我们组合了两个函数,实际上我们也可以组合 N 个函数;

finalValue <-- func1 <-- func2 <-- ... <-- funcN <-- origValue

JS组合函数实例分析

比如用一个 compose 函数来实现(敲重点):

function compose(...fns) {    return function composed(result){        // 拷贝一份保存函数的数组        var list = fns.slice();        while (list.length > 0) {            // 将最后一个函数从列表尾部拿出            // 并执行它            result = list.pop()( result );        }        return result;    };}// ES6 箭头函数形式写法var compose =    (...fns) =>        result => {            var list = fns.slice();            while (list.length > 0) {                // 将最后一个函数从列表尾部拿出                // 并执行它                result = list.pop()( result );            }            return result;        };

基于前面 uniqueWords(..) 的例子,我们进一步再增加一个函数来处理(过滤掉长度小于等于4的字符串):

function skipShortWords(list) {    var filteredList = [];    for (let i = 0; i < list.length; i++) {        if (list[i].length > 4) {            filteredList.push( list[i] );        }    }    return filteredList;}var text = "To compose two functions together";var biggerWords = compose( skipShortWords, unique, words );var wordsUsed = biggerWords( text );wordsUsed;// ["compose", "functions", "together"]

这样 compose 函数就有三个入参且都是函数了。我们还可以利用偏函数的特性实现更多:

function skipLongWords(list) {  }var filterWords = partialRight( compose, unique, words ); // 固定 unique 函数 和 words 函数var biggerWords = filterWords( skipShortWords );var shorterWords = filterWords( skipLongWords );biggerWords( text );shorterWords( text );

filterWords 函数是一个更具有特定功能的变体(根据第一个函数的功能来过滤字符串)。

compose 变体

compose(..)函数非常重要,但我们可能不会在生产中使用自己写的 compose(..),而更倾向于使用某个库所提供的方案。了解其底层工作的原理,对我们强化理解函数式编程也非常有用。

我们理解下 compose(..) 的另一种变体 —— 递归的方式实现:

function compose(...fns) {    // 拿出最后两个参数    var [ fn1, fn2, ...rest ] = fns.reverse();    var composedFn = function composed(...args){        return fn2( fn1( ...args ) );    };    if (rest.length == 0) return composedFn;    return compose( ...rest.reverse(), composedFn );}// ES6 箭头函数形式写法var compose =    (...fns) => {        // 拿出最后两个参数        var [ fn1, fn2, ...rest ] = fns.reverse();        var composedFn =            (...args) =>                fn2( fn1( ...args ) );        if (rest.length == 0) return composedFn;        return compose( ...rest.reverse(), composedFn );    };

通过递归进行重复的动作比在循环中跟踪运行结果更易懂,这可能需要更多时间去体会;

基于之前的例子,如果我们想让参数反转:

var biggerWords = compose( skipShortWords, unique, words );// 变成var biggerWords = pipe( words, unique, skipShortWords );

只需要更改 compose(..) 内部实现这一句就行:

...        while (list.length > 0) {            // 从列表中取第一个函数并执行            result = list.shift()( result );        }...

虽然只是颠倒参数顺序,这二者没有本质上的区别。

抽象能力

你是否会疑问:什么情况下可以封装成上述的“盒子”呢?

这就很考验 —— 抽象的能力了!

实际上,有两个或多个任务存在公共部分,我们就可以进行封装了。

比如:

function saveComment(txt) {    if (txt != "") {        comments[comments.length] = txt;    }}function trackEvent(evt) {    if (evt.name !== undefined) {        events[evt.name] = evt;    }}

就可以抽象封装为:

function storeData(store,location,value) {    store[location] = value;}function saveComment(txt) {    if (txt != "") {        storeData( comments, comments.length, txt );    }}function trackEvent(evt) {    if (evt.name !== undefined) {        storeData( events, evt.name, evt );    }}

在做这类抽象时,有一个原则是,通常被称作 DRY(don't repeat yourself),即便我们要花时间做这些非必要的工作。

抽象能让你的代码走得更远! 比如上例,还能进一步升级:

function conditionallyStoreData(store,location,value,checkFn) {    if (checkFn( value, store, location )) {        store[location] = value;    }}function notEmpty(val) { return val != ""; }function isUndefined(val) { return val === undefined; }function isPropUndefined(val,obj,prop) {    return isUndefined( obj[prop] );}function saveComment(txt) {    conditionallyStoreData( comments, comments.length, txt, notEmpty );}function trackEvent(evt) {    conditionallyStoreData( events, evt.name, evt, isPropUndefined );}

这样 if 语句也被抽象封装了。

抽象是一个过程,程序员将一个名字与潜在的复杂程序片段关联起来,这样该名字就能够被认为代表函数的目的,而不是代表函数如何实现的。通过隐藏无关的细节,抽象降低了概念复杂度,让程序员在任意时间都可以集中注意力在程序内容中的可维护子集上。—— 《程序设计语言》

我们在本系列初始提到:“一切为了创造更可读、更易理解的代码。”

从另一个角度,抽象就是将命令式代码变成声命式代码的过程。从“怎么做”转化成“是什么”。

命令式代码主要关心的是描述怎么做来准确完成一项任务。声明式代码则是描述输出应该是什么,并将具体实现交给其它部分。

比如 ES6 增加的结构语法:

function getData() {    return [1,2,3,4,5];}// 命令式var tmp = getData();var a = tmp[0];var b = tmp[3];// 声明式var [ a ,,, b ] = getData();

“JS组合函数实例分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

JS组合函数实例分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

JS组合函数实例分析

本篇内容介绍了“JS组合函数实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!组合函数含义函数编程就像拼乐高!乐高有各式各样的零部件,我
2023-07-02

vue封装组件js实例分析

本文小编为大家详细介绍“vue封装组件js实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue封装组件js实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。什么是组件化:组件化就是将一个页面拆分成一
2023-06-30

LINQ函数集合的示例分析

这篇文章主要为大家展示了“LINQ函数集合的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“LINQ函数集合的示例分析”这篇文章吧。LINQ函数集合我已经在C#3.0的介绍LINQ专题中提
2023-06-17

JS函数防抖和函数节流的示例分析

这篇文章主要介绍JS函数防抖和函数节流的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!概述函数防抖和函数节流都是定义一个函数,该函数接收一个函数作为参数,并返回一个添加了防抖或节流功能后的函数。因此可以将函数
2023-06-15

JS前端面试数组扁平化手写flat函数示例分析

本文小编为大家详细介绍“JS前端面试数组扁平化手写flat函数示例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“JS前端面试数组扁平化手写flat函数示例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。题
2023-07-02

Python函数参数实例分析

本文小编为大家详细介绍“Python函数参数实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“Python函数参数实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.函数参数# 1.位置参数:调用函数
2023-06-28

java数组的实例分析

这篇文章主要介绍“java数组的实例分析”,在日常操作中,相信很多人在java数组的实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java数组的实例分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧
2023-06-17

C++函数参数实例分析

这篇文章主要介绍了C++函数参数实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C++函数参数实例分析文章都会有所收获,下面我们一起来看看吧。一、函数参数的默认值C++ 中可以在函数声明时为参数提供一个默
2023-06-30

Java二维数组实例分析

这篇文章主要介绍了Java二维数组实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java二维数组实例分析文章都会有所收获,下面我们一起来看看吧。什么是数组数组(Array)是有序的元素序列。 若将有限个
2023-06-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录