javascript函数式编程程序员的工具集


Posted in Javascript onOctober 11, 2015

如果你仔细看了到目前为止出现过的示例代码,你会发现这里面的一些方法不太熟悉。 它们是map()、filter()和reduce()函数,它们对任何语言的函数式编程都至关重要。 它们可以让你不必使用循环和语句,写出更简洁的代码。

map()、filter()和reduce()函数组成了函数式程序员工具集的核心部分,这个工具集包括一系列纯的、 高阶的函数,它们是函数式方法的主力。实际上,它们是纯函数和高阶函数的典型,它们以一个函数为输入, 返回一个输出结果,并且不产生副作用。

然而它们是浏览器中ECMAScript 5.1的实现标准,它们只工作于数组。每次调用它们,一个新的数组会被创建并返回, 而原来存在的那个数组不会被改变。它们以函数为输入,经常使用匿名函数作为回调函数。它们遍历数组, 并对数组的每一个元素应用这个函数!

myArray = [1,2,3,4];
newArray = myArray.map(function(x) {return x*2});
console.log(myArray); // Output: [1,2,3,4]
console.log(newArray); // Output: [2,4,6,8]

还有一点,它们只作用于数组,无法作用于其它可迭代的数据结构,比如对象。不用担心, 有很多库比如Underscore.js,Lazy.js,stream.js等等都实现了它们自己的更强大的map()、 filter()和reduce()。

回调

如果你以前从来没用过回调,那这个概念可能会让你有些迷惑。尤其是在Javascript中, Javascript给出了好几种声明函数的方式。

回调函数用于传递给另外一个函数供它们使用,这是一种像传递对象一样来传递逻辑的方式:

var myArray = [1,2,3];
function myCallback(x){return x+1};
console.log(myArray.map(myCallback));

对于比较简单的任务可以用匿名函数:

console.log(myArray.map(function(x){return x+1}));

回调不仅用于函数式编程,在Javascript中它们能干很多事情。仅作为例子,这有个callback()函数用于jQuery的AJAX调用:

function myCallback(xhr) {
 console.log(xhr.status);
 return true;
}
$.ajax(myURI).done(myCallback);

注意这里只用了函数的名字,因为我们并不是要调用函数而是传递函数,写成这样就错了:

$.ajax(myURI).fail(myCallback(xhr)); 
// 或者
$.ajax(myURI).fail(myCallback());

如果我们调用了函数会发生什么?在这个例子里,myCallback(xhr)会尝试执行,控制台将打印“undefined”, 并会返回true。当ajax()完成调用时,它根据名字找到的回调函数将是一个"true",然后就报错了。

也就是说我们无法指定给回调函数传什么参数,如果我们的回调函数需要让ajax()函数传给他我们想要的参数, 我们可以把回到函数包在一个匿名函数里:

function myCallback(status) {
 console.log(status);
 return true;
}
$.ajax(myURI).done(function(xhr) {
 myCallback(xhr.status)
});

Array.prototype.map()

map()是这些函数的老大,它简单地对数组里的元素依此应用回调函数。

语法:arr.map(callback [, thisArg]);

参数:
•callback(): 这个函数为新数组产生一个元素,它接收的参数: ◦currentValue:数组当前遍历到的元素
◦index:数组中当前元素序数
◦array:当前正在处理的数组

•thisArg:这是个可选参数,当执行回调的时候它作为回调函数的this

例子:

var
 integers = [1, -0, 9, -8, 3],
 numbers = [1, 2, 3, 4],
 str = 'hello world how ya doing?';
 
// 将整数映射为他们自己的绝对值
console.log(integers.map(Math.abs));

// 将数组中的元素与自己的位置序数相乘
console.log(numbers.map(function(x, i) {
 return x * i
}));
// 单词隔一个变一个大写
console.log(str.split(' ').map(function(s, i) {
 if (i % 2 == 0)
  return s.toUpperCase();
 else
  return s;
}));

尽管Array.prototype.map方法是Javascript中数组对象的标准方法,你也可以很容易地扩展自己的对象。

MyObject.prototype.map = function(f) {
  return new MyObject(f(this.value));
 };

Array.prototype.filter()

filter()函数用于把数组中的一些元素筛选出来。回调函数必须返回真(保留到新数组里)或假(扔掉)。 用map()可以做类似的事情,就是把你像扔掉的元素返回为null,不过filter()函数会在新数组里面删除这些不要的元素, 而不是留个null占着位置。

语法:arr.filter(callback [, thisArg]);

•callback():这个函数用来测试数组中的每个元素,要保留返回真,否则返回假。它有这些参数: ◦currentValue:数组当前遍历到的元素
◦index:数组中当前元素的序数
◦array:当前正在处理的数组

•thisArg:这是个可选参数,当执行回调的时候它作为回调函数的this

例子:

var myarray = [1, 2, 3, 4]
words = 'hello 123 world how 345 ya doing'.split(' ');
re = '[a-zA-Z]';
// 筛选整数
console.log([-2, -1, 0, 1, 2].filter(function(x) {
 return x > 0
}));
// 筛选所有含字母的单词
console.log(words.filter(function(s) {
 return s.match(re);
}));
// 随机移除数组中的元素
console.log(myarray.filter(function() {
 return Math.floor(Math.random() * 2)
}));

Array.prototype.reduce()

reduce()函数,有时也称为fold,它用于把数组中的所有值聚集到一起。回调需要返回组合对象的逻辑。 对于数字来说,它们往往会被加到一起或者乘到一起。对于字符串来说,它们往往是被追加到一起。

语法:arr.reduce(callback [, initialValue]);

参数
•callback():此函数把两个对象合并成一个对象,并将其返回。参数有: ◦previousValue:上一次回调函数被调用时返回的值,或者是初始值(如果有的话)
◦currentValue:数组当前正在处理的元素
◦index:数组中当前元素的序数
◦array:当前正在处理的数组

•initialValue:可选。第一次回调所传入参数的初始值

例子

var numbers = [1, 2, 3, 4];

// 把数组中所有的值加起来
console.log([1, 2, 3, 4, 5].reduce(function(x, y) {
 return x + y
}, 0));

// 查找数组中最大的值
console.log(numbers.reduce(function(a, b) {
  return Math.max(a, b) // max()函数只能有两个参数
 }) 
);

其它函数

map()、filter()和reduce()函数在我们辅助函数的工具箱里并不孤单。这里还有更多的函数几乎在所有函数式应用里都会被使用。

Array.prototype.forEach

forEach()函数本质上是map()函数的非纯版本,它会遍历整个数组,并对每个元素应用回调。 然而这些回调函数不返回值。它是实现for循环的一个更纯粹的方式。

语法:arr.forEach(callback [, thisArg]);

参数:
•callback():对数组中每一个元素所应用的。参数有: ◦currentValue:数组中当前正在处理的元素
◦index:数组中当前元素的序数
◦array:正在处理的数组

•thisArg:可选。回调函数中作为this的值

例子:

var arr = [1, 2, 3];
var nodes = arr.map(function(x) {
 var elem = document.createElement("div");
 elem.textContent = x;
 return elem;
});

// 对每一个元素的值输出日志
arr.forEach(function(x) {
 console.log(x)
});

// 把节点追加到DOM上
nodes.forEach(function(x) {
 document.body.appendChild(x)
});

Array.prototype.concat

如果不用for或while处理数组,你会经常需要把数组拼接起来。另一个Javascript内建函数concat就是专门干这事儿的。 concat函数会返回一个新数组但不改变旧数组。它可以把你传入的所有参数拼接到一起。
console.log([1, 2, 3].concat(['a','b','c']) // 拼接两个数组
// Output: [1, 2, 3, 'a','b','c']

它返回两个数组拼接成的数组,同时原来的那些数组没有被改变。这就意味着concat函数可以链式调用。

var arr1 = [1,2,3];
var arr2 = [4,5,6];
var arr3 = [7,8,9];
var x = arr1.concat(arr2, arr3);
var y = arr1.concat(arr2).concat(arr3));
var z = arr1.concat(arr2.concat(arr3)));
console.log(x);
console.log(y);
console.log(z);

变量x、y、z的值最后都是[1,2,3,4,5,6,7,8,9]。

Array.prototype.reverse

这个Javascript内建函数是用于数组变形的。reverse函数用于将一个数组反转,也就是第个一元素会跑到最后, 而最后一个元素变成了第一个元素。

然而,这个函数并不会返回一个新的数组,而是把原来的数组替换掉了。我们可以做个更好的。下面是一个纯的反转数组函数

var invert = function(arr) {
 return arr.map(function(x, i, a) {
  return a[a.length - (i + 1)];
 });
};
var q = invert([1, 2, 3, 4]);
console.log(q);

Array.prototype.sort

与map()、filter()和reduce()函数相似,排序函数sort()需要传入一个回调函数来定义数组如何排序。 但是,跟reverse()一样,它也会把原来的数组替换。这可不太好。
arr = [200, 12, 56, 7, 344];
console.log(arr.sort(function(a,b){return a?b}) );
// arr现在是: [7, 12, 56, 200, 344];

我们可以写一个纯函数的sort(),但是排序算法的源代码很麻烦。对于特别大的数组,应当根据特定的数据结构来选用适合的算法, 比如快速排序、合并排序、冒泡排序等等。

Array.prototype.every 和 Array.prototype.some

Array.prototype.every() 和 Array.prototype.some() 都是纯的高阶函数,它们是Array对象的方法, 通过回调函数根据数组各元素返回的布尔值(或相当于布尔的值)来进行测试。如果数组中所有的元素通过回调函数计算都返回True, every()函数就返回true;如果数组中有一个元素返回True,some()函数就返回True。

例子:

function isNumber(n) {
 return !isNaN(parseFloat(n)) && isFinite(n);
}
console.log([1, 2, 3, 4].every(isNumber)); // Return: true
console.log([1, 2, 'a'].every(isNumber)); // Return: false
console.log([1, 2, 'a'].some(isNumber)); // Return: true
Javascript 相关文章推荐
Javascript学习笔记 delete运算符
Sep 13 Javascript
jQuery调用AJAX时Get和post公用的乱码解决方法实例说明
Jun 04 Javascript
类似php的js数组的in_array函数自定义方法
Dec 27 Javascript
JQuery中操作Css样式的方法
Feb 12 Javascript
微信小程序-详解数据缓存
Nov 24 Javascript
BootStrap Validator 版本差异问题导致的submitHandler失效问题的解决方法
Dec 01 Javascript
javascript工厂模式和构造函数模式创建对象方法解析
Dec 30 Javascript
vue2.0 路由不显示router-view的解决方法
Mar 06 Javascript
Vue 监听列表item渲染事件方法
Sep 06 Javascript
vue实现表单录入小案例
Sep 27 Javascript
vue使用echarts实现水平柱形图实例
Sep 09 Javascript
vue.js Router中嵌套路由的实用示例
Jun 27 Vue.js
深入探讨javascript函数式编程
Oct 11 #Javascript
Javascript函数式编程语言
Oct 11 #Javascript
Javascript函数式编程简单介绍
Oct 11 #Javascript
jQuery实现仿新浪微博浮动的消息提示框(可智能定位)
Oct 10 #Javascript
JS+DIV+CSS排版布局实现美观的选项卡效果
Oct 10 #Javascript
JS实现漂亮的窗口拖拽效果(可改变大小、最大化、最小化、关闭)
Oct 10 #Javascript
JavaScript实现的浮动层框架用法实例分析
Oct 10 #Javascript
You might like
Codeigniter通过SimpleXML将xml转换成对象的方法
2015/03/19 PHP
php实现的二分查找算法示例
2017/06/20 PHP
Yii Framework框架使用PHPExcel组件的方法示例
2019/07/24 PHP
PHP实现统计代码行数小工具
2019/09/19 PHP
Laravel5.1 框架响应基本用法实例分析
2020/01/04 PHP
picChange 图片切换特效的函数代码
2010/05/06 Javascript
从jQuery.camelCase()学习string.replace() 函数学习
2011/09/13 Javascript
jquery实现选中单选按钮下拉伸缩效果
2015/08/06 Javascript
纯HTML5制作围住神经猫游戏-附源码下载
2015/08/23 Javascript
JS+CSS实现另类带提示效果的竖向导航菜单
2015/10/15 Javascript
基于jquery实现图片相关操作(重绘、获取尺寸、调整大小、缩放)
2015/12/25 Javascript
关于webuploader插件使用过程遇到的小问题
2016/11/07 Javascript
jQuery如何跳转到另一个网页 就这么简单
2016/12/28 Javascript
jquery实现文字单行横移或翻转(上下、左右跳转)
2017/01/08 Javascript
Webpack实现按需打包Lodash的几种方法详解
2017/05/08 Javascript
详解windows下vue-cli及webpack 构建网站(三)使用组件
2017/06/17 Javascript
解决layui前端框架 form表单,table表等内置控件不显示的问题
2018/08/19 Javascript
微信小程序进入广告实现代码实例
2019/09/19 Javascript
深入解读VUE中的异步渲染的实现
2020/06/19 Javascript
JS+css3实现幻灯片轮播图
2020/08/14 Javascript
如何使用gpu.js改善JavaScript的性能
2020/12/01 Javascript
[09:43]DOTA2每周TOP10 精彩击杀集锦vol.5
2014/06/25 DOTA
python3去掉string中的标点符号方法
2019/01/22 Python
python 实现兔子生兔子示例
2019/11/21 Python
python输出数学符号实例
2020/05/11 Python
实例讲解Python 迭代器与生成器
2020/07/08 Python
阿里云:Aliyun.com
2017/02/15 全球购物
美国最大和最受信任的二手轮胎商店:Bestusedtires.com
2020/06/02 全球购物
总经理助理工作职责
2014/02/06 职场文书
益达广告词
2014/03/14 职场文书
农村产权制度改革实施方案
2014/03/21 职场文书
班子个人四风问题整改措施
2014/10/04 职场文书
我的长征观后感
2015/06/09 职场文书
Java图书管理系统,课程设计必用(源码+文档)
2021/06/30 Java/Android
PostGIS的安装与入门使用指南
2022/01/18 PostgreSQL
vue报错function () { [native code] },无法出现我们想要的内容 Unknown custom element
2022/04/11 Vue.js