《JavaScript函数式编程》读后感


Posted in Javascript onAugust 07, 2015

本文章记录本人在学习 函数式 中理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习。

在近期看到了《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的函数式编程。下面引用书上的总结:

确定抽象,并为其构建函数。
利用已有的函数来构建更加复杂的抽象。
通过将现有的函数传给其他的函数来构建更加复杂的抽象。
单是构建抽象还是不够的,如果能够把强大的数据抽象结合来实现函数式编程效果会更加好。

后面的章节读后感会慢慢的分享给大家,敬请关注。

Javascript 相关文章推荐
使用javascript实现监控视频播放并打印日志
Jan 05 Javascript
jQuery实现仿Alipay支付宝首页全屏焦点图切换特效
May 04 Javascript
jquery append 动态添加的元素事件on 不起作用的解决方案
Jul 30 Javascript
快速学习jQuery插件 jquery.validate.js表单验证插件使用方法
Dec 01 Javascript
javascript实现鼠标点击页面 移动DIV
Dec 02 Javascript
jquery submit()不能提交表单的解决方法
Apr 24 jQuery
使用jQuery实现购物车结算功能
Aug 15 jQuery
利用jQuery+localStorage实现一个简易的计时器示例代码
Dec 25 jQuery
vue之将echart封装为组件
Jun 02 Javascript
vue 监听键盘回车事件详解 @keyup.enter || @keyup.enter.native
Aug 25 Javascript
jquery实现动态改变css样式的方法分析
May 27 jQuery
微信小程序swiper实现文字纵向轮播提示效果
Jan 21 Javascript
jQuery过滤HTML标签并高亮显示关键字的方法
Aug 07 #Javascript
jquery实现先淡出再折叠收起的动画效果
Aug 07 #Javascript
C++中的string类的用法小结
Aug 07 #Javascript
Grunt入门教程(自动任务运行器)
Aug 06 #Javascript
PhantomJS快速入门教程(服务器端的 JavaScript API 的 WebKit)
Aug 06 #Javascript
jQuery实现hover合成事件的方法
Aug 06 #Javascript
Ajax清除浏览器js、css、图片缓存的方法
Aug 06 #Javascript
You might like
php使用json_encode对变量json编码
2014/04/07 PHP
ThinkPHP CURD方法之limit方法详解
2014/06/18 PHP
Vagrant(WSL)+PHPStorm+Xdebu 断点调试环境搭建
2019/12/13 PHP
由JavaScript中call()方法引发的对面向对象继承机制call的思考
2011/09/12 Javascript
angularJS 入门基础
2015/02/09 Javascript
JavaScript对数组进行随机重排的方法
2015/07/22 Javascript
利用原生js和jQuery实现单选框的勾选和取消操作的方法
2016/09/04 Javascript
基于JS分页控件实现简单美观仿淘宝分页按钮效果
2016/11/07 Javascript
Javascript使用function创建类的两种方法(推荐)
2016/11/19 Javascript
js 动态生成json对象、时时更新json对象的方法
2016/12/02 Javascript
Javascript之面向对象--方法
2016/12/02 Javascript
bootstrap table之通用方法( 时间控件,导出,动态下拉框, 表单验证 ,选中与获取信息)代码分享
2017/01/24 Javascript
深入理解基于vue-cli的vuex配置
2017/07/24 Javascript
详解webpack4升级指南以及从webpack3.x迁移
2018/06/12 Javascript
VUE预渲染及遇到的坑
2018/09/03 Javascript
electron实现qq快捷登录的方法示例
2018/10/22 Javascript
JS实现的新闻列表自动滚动效果示例
2019/01/30 Javascript
angular中如何绑定iframe中src的方法
2019/02/01 Javascript
用JS实现一个简单的打砖块游戏
2019/12/11 Javascript
nuxt.js添加环境变量,区分项目打包环境操作
2020/11/06 Javascript
[01:48]帕吉至宝加入游戏,遗迹战场现“千劫神屠”
2018/04/07 DOTA
详解Django中的权限和组以及消息
2015/07/23 Python
Python程序员面试题 你必须提前准备!
2018/01/16 Python
python 将列表中的字符串连接成一个长路径的方法
2018/10/23 Python
详解Python sys.argv使用方法
2019/05/10 Python
django 连接数据库出现1045错误的解决方式
2020/05/14 Python
HTML5移动端手机网站开发流程
2016/04/25 HTML / CSS
h5页面背景图很长要有滚动条滑动效果的实现
2021/01/27 HTML / CSS
双十佳事迹材料
2014/01/29 职场文书
2014年连锁店圣诞节活动方案
2014/12/09 职场文书
成绩单家长意见
2015/06/03 职场文书
升职自荐书
2019/05/09 职场文书
话题作文之成长
2019/12/09 职场文书
Oracle表空间与权限的深入讲解
2021/11/17 Oracle
MySQL数据库完全卸载的方法
2022/03/03 MySQL
vue实现列表拖拽排序的示例代码
2022/04/08 Vue.js