摘要:在近期看到了函数式编程这本书预售的时候就定了下来。主要目的是个人目前还是不理解什么是函数式编程。且和现在在学习函数式编程有莫大的关系。加速大概了解了函数式编程之后。总结看完了第一章也是可以小结一下的函数式编程。
本文章记录本人在学习 函数式 中理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习。
在近期看到了《JavaScript函数式编程》这本书预售的时候就定了下来。主要目的是个人目前还是不理解什么是函数式编程。在自己学习的过程中一直听到身边的人说面向过程编程和面向对象编程,而函数式就非常少。为了自己不要落后于其他同学的脚步,故想以写笔记的方式去分享和记录自己阅读中所汲取的知识。
js 和函数式编程书中用了一句简单的话来回答了什么是函数式编程:
函数式编程通过使用函数来将值转换为抽象单元,接着用于构建软件系统。
我觉得一定有同学看了这一句还是不怎么动什么是函数式编程,且为什么要使用函数式编程。后面的很多例子都使用到了Underscore。
以函数为抽象单元抽象方法是指隐藏了细节的函数。举一个书中的例子,一个检测输出年龄值的函数(主要是关于错误和警告的报告):
function parseAge(age) { if (!_.isString(age)) throw new Error("Expecting a string"); var a; console.log("Attempting to parse an age"); a = parseInt(age, 10); if (_.isNaN(a)) { console.log(["Could not parse age: "].join()); a = 0; } return a; }
上面的函数判断我们是不是输入一个年龄,且必须是字符串形式。接着就是来运行这个函数:
parseAge("42"); //=> 42 parseAge(42); //=> Error:Expecting a string parseAge("hhhh"); //=> 0
上面的parseAge函数工作正常没有什么问题。如果我们要修改输出错误=信息和警告的呈现方式、那么就需要修改相应的代码行,以及其他地方的输出模式。书中给的方法是通过将它们抽象成不同的函数来实现:
function fail(thing) { throw new Error(thing); } function warn(thing) { console.log(["WARNING:", thing].join("")); } function note(thing) { console.log(["NOTE:", thing].join("")); }
接着就是使用上面的函数,去重构parseAge这个函数。
funciton parseAge(age) { if (!_.isString(age)) fail("Expecting a string"); var a; note("Attempting to parse an age"); a = parseInt(age, 10); if (_.isNaN(a)) { warn(["Could not parse age:", age].join("")); a = 0; } return a; }
把报告错误的代码都放到不同的函数里去,且重构后的parseAge和之前的也没有多大的变化。但是不同的就是现在报告错误、信息和警告的想法已经被抽象化。错误、信息和警告的报告结果也是完全被修改了。
这么做是,由于行为包含在单一的函数中,所以函数可以被能够提供类似行为的新函数取代,或直接被完全不同的行为取代。
封装和隐蔽这个标题很容易理解,举个例子。像我们经常使用iife来避免全局的污染,这就是一个封装和隐蔽的很好例子。通过使用iife来隐蔽自己的写的一些变量和方法,目的就是不去污染全局的环境。这也是使用闭包的方式来隐蔽数据。
因为闭包也是一种函数。且和现在在学习函数式编程有莫大的关系。但是也不要忘记了之前学习的面向对象式封装,毕竟这两者不能说谁更加的好。但是都掌握了也不是一件什么坏事。一句老话:看需求。
以函数为行为单位隐藏数据和行为(通常不方便于快速修改)只是一种讲函数作为抽象单元的方式。另一种方式就是提供一种简单地存储方式和传递基本行为的离线散单元。
书中一个小栗子,通过使用js语法来索引数组中的一个值:
var arr = ["a", "b", "c"]; arr[1] //=> b
虽然上面索引数组中的中的一个值很简单,但并没有办法可以在不把它放到函数里的前提下,获取这个行为并根据需要来使用他/她。写一个简单函数nth,用来索引数组中的一个值:
function nth(a, index) { return a[index]; }
接着运行:
nth(arr, 1); //=> b
运行成功,但是如果传入一个空对象时,就会报错了。因此,如果想围绕nth来实现函数抽象,我们或许会设计下面的声明:nth返回一个存储在允许索引访问的数据类型中的有效袁术。这段声明的关键在于索引数据的类型的概念。或许需要一个函数来判断类型:
function isIndexed(data) { return _.isArray(data) || _.isString(data); }
接着继续完善nth函数。isIndexed函数是一个提供了判断某个数据是否为字符串或者数组的抽象。
function nth(a, index) { if (!_.isNumber(index)) fail("Expected a number as the index"); if (!isIndexed(a)) fail("Not supported on non-indexed type"); if ((index < 0) || (index > a.length - 1)) fail("Index value is out of bounds"); return a[index]; }
从index抽对象构建nth函数抽象的方式一样,也可以以同样的方式来构建一个second抽象:
function second(a) { return nth(a, 1); }
函数second允许在一个不同但相关的情况下,正确的使用nth函数:
second(arr); //=> b
通过上面的栗子,就知道。我们可以把每一步都抽象成一个函数,把每一个参数都抽象出来。虽然这样写感觉定义了许多函数。不过这样更加容易理解每一项的功能和流程。
数据抽象JavaScript 的对象原型模型是一个丰富且基础的数据方案。
因为js没有类的原因,就有了许多模拟类的方法,且在ES6上也出现了class关键字。尽管类有许多长处,但很多的时候js应用程序的数据需求币类中的简单的要多。
基于类的对象系统的一个有理的论据是实现用户界面的历史使用。
js中的对象和数组已经能够满足我们对数据的操作了,且Underscore也是重点也是如何处理数组和对象。
实施和使用的简易性是使用js的核心数据结构进行数据建模的目的。这并不是说面向对象或者基于类的方法就完全没有用。处理集合为中心的函数式方式更加适合处理与人有关的数据,而面向对象的方法最适合模拟人。
js函数式初试在开始函数式编程前,需要先定义两个常用且有用的函数:
function existy(x) { return x != null } function truthy(x) { return (x !== false) && existy(x); }
existy函数旨在定义事物之前的存在。js中就有两个值可以表示不存在:null和undefined。
truthy函数用来判断一个对象是否应该认为是true的同义词。
我们可以在很多地方使用到这两个函数,其实函数式理念来自于它们的使用。有些同学可能已经熟悉了许多js实现中的map forEach等方法。且Underscroe也提供了许多类似的方法,这也许就是选择Underscroe来辅助学习函数式编程的原因。
简单说下就是:
一个对”存在“的抽象函数的定义。
一个建立在存在函数之上的,对”真“的抽象函数定义。
通过其他函数来使用上面的两个函数,以实现更多的行为。
加速大概了解了函数式编程之后。你可能会想这函数式编程不是很慢吗?比如前面获取数组索引,有必要定义一个函数来专门获取吗?直接用arr[index]绝对比那些函数来的快。
var arr = [1, 2, 3, 4, 5]; // 最快 for (var i = 0; i < arr.length; i++) { console.log(arr[i]); } // 较慢 _.each(arr, function (val, index) { console.log(index); });
但是我们在写代码的时候可能不会考虑的那么深,也许使用函数的确比原生要慢一些。但是大多数情况下也不会去在乎那么点时间,且现在有强大的v8引擎,大部分情况下的他都能很高效的编译和执行我们的js代码。所以我们没有必要在还没有写出正确的代码前考虑运算速度。
如果是我来选择的话,可能会更加关注与代码的风格。那种写法写的舒服看的舒服就使用哪一种,当然也是要保证基本的运算速度下,以不至于慢的离谱。看的舒服的代码比跑的快的代码可能更加有成就感。
总结看完了第一章也是可以小结一下js的函数式编程。下面引用书上的总结:
确定抽象,并为其构建函数。
利用已有的函数来构建更加复杂的抽象。
通过将现有的函数传给其他的函数来构建更加复杂的抽象。
单是构建抽象还是不够的,如果能够把强大的数据抽象结合来实现函数式编程效果会更加好。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/85864.html
摘要:不纯的函数充满的不确定性,在函数式编程中要尽量避免它。在以后的函数式编程中还会不断的遇见它的。 一.为什么需要柯里化(curry函数) 1.先简单的介绍一下纯函数 在函数式编程中纯函数是其最基本的思想,所谓纯函数就是一个相对不受外界影响(之所以说相对,是因为有的时候需要和外界沟通,函数没法保持所谓真正的纯,但后面会有方法来解决).在高中数学中都学过,函数是一种映射关系,在y=f(x)这...
摘要:返回一个函数作为结果。利用函数将值的集合合并成一个值,该函数接受一个累积和本次处理的值。集合中心编程函数式编程对于需要操作集合中元素的任务非常有用。 本文章记录本人在学习 函数式 中理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习。 函数是一等公民 一等这个术语通常用来描述值。当函数被看作一等公民时,那它就可以去任何值可以去的地方,很少有限制。比如那数值和函数做...
摘要:设定浏览器属性的属性的方法叫做驼峰式命名是函数名方法名和对象属性名的命名首选格式。由浏览器预先定义的对象被称为宿主对象。在给某个元素添加了事件处理函数后,一旦事件发生,相应的代码就会执行。 1.JavaScript是一个使网页具有交互能力的程序设计语言。 2.设定浏览器属性的属性的方法叫做BOM. 3.驼峰式命名(myMood)是函数名、方法名和对象属性名的命名首选格式。 4.命名变量...
摘要:限制编写并行流,存在一些与非并行流不一样的约定。底层框架并行流在底层沿用的框架,递归式的分解问题,然后每段并行执行,最终由合并结果,返回最后的值。 本书第六章的读书笔记,也是我这个系列的最后一篇读书笔记。后面7、8、9章分别讲的测试、调试与重构、设计和架构的原则以及使用Lambda表达式编写并发程序,因为笔记不好整理,就不写了,感兴趣的同学自己买书来看吧。 并行化流操作 关于并行与并发...
摘要:但是,最好使用差异化的类型定义,函数签名如下其实二者说的是同一件事。后者的返回值和初始函数的返回值相同,即。破坏式更新和函数式更新的比较三的延迟计算的设计者们在将引入时采取了比较特殊的方式。四匹配模式语言中暂时并未提供这一特性,略。 一、无处不在的函数 一等函数:能够像普通变量一样使用的函数称为一等函数(first-class function)通过::操作符,你可以创建一个方法引用,...
阅读 1877·2021-09-27 13:35
阅读 3431·2019-08-30 14:16
阅读 2485·2019-08-30 10:52
阅读 860·2019-08-29 16:35
阅读 1418·2019-08-29 15:22
阅读 3642·2019-08-23 18:21
阅读 3134·2019-08-23 18:00
阅读 3124·2019-08-23 16:50