深入学习JavaScript 高阶函数


Posted in Javascript onJune 11, 2019

高阶函数

高阶函数英文叫 Higher-order function,它的定义很简单,就是至少满足下列一个条件的函数:

  • 接受一个或多个函数作为输入
  • 输出一个函数

也就是说高阶函数是对其他函数进行操作的函数,可以将它们作为参数传递,或者是返回它们。 简单来说,高阶函数是一个接收函数作为参数传递或者将函数作为返回值输出的函数。

函数作为参数传递

JavaScript 语言中内置了一些高阶函数,比如 Array.prototype.map,Array.prototype.filter 和 Array.prototype.reduce,它们接受一个函数作为参数,并应用这个函数到列表的每一个元素。我们来看看使用它们与不使用高阶函数的方案对比。

Array.prototype.map

map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果,原始数组不会改变。传递给 map 的回调函数(callback)接受三个参数,分别是 currentValue、index(可选)、array(可选),除了 callback 之外还可以接受 this 值(可选),用于执行 callback 函数时使用的this 值。

来个简单的例子方便理解,现在有一个数组 [1, 2, 3, 4],我们想要生成一个新数组,其每个元素皆是之前数组的两倍,那么我们有下面两种使用高阶和不使用高阶函数的方式来实现。

不使用高阶函数

// 木易杨
const arr1 = [1, 2, 3, 4];
const arr2 = [];
for (let i = 0; i < arr1.length; i++) {
arr2.push( arr1[i] * 2);
}
console.log( arr2 );
// [2, 4, 6, 8]
console.log( arr1 );
// [1, 2, 3, 4]

使用高阶函数

// 木易杨
const arr1 = [1, 2, 3, 4];
const arr2 = arr1.map(item => item * 2);
console.log( arr2 );
// [2, 4, 6, 8]
console.log( arr1 );
// [1, 2, 3, 4]

Array.prototype.filter

filter() 方法创建一个新数组, 其包含通过提供函数实现的测试的所有元素,原始数组不会改变。接收的参数和 map 是一样的,其返回值是一个新数组、由通过测试的所有元素组成,如果没有任何数组元素通过测试,则返回空数组。

来个例子介绍下,现在有一个数组 [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4],我们想要生成一个新数组,这个数组要求没有重复的内容,即为去重。

不使用高阶函数

const arr1 = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
const arr2 = [];
for (let i = 0; i < arr1.length; i++) {
if (arr1.indexOf( arr1[i] ) === i) {
arr2.push( arr1[i] );
}
}
console.log( arr2 );
// [1, 2, 3, 5, 4]
console.log( arr1 );
// [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4]

使用高阶函数

const arr1 = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
const arr2 = arr1.filter( (element, index, self) => {
return self.indexOf( element ) === index;
});
console.log( arr2 );
// [1, 2, 3, 5, 4]
console.log( arr1 );
// [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4]

Array.prototype.reduce

reduce() 方法对数组中的每个元素执行一个提供的 reducer 函数(升序执行),将其结果汇总为单个返回值。传递给 reduce 的回调函数(callback)接受四个参数,分别是累加器 accumulator、currentValue、currentIndex(可选)、array(可选),除了 callback 之外还可以接受初始值 initialValue 值(可选)。

  • 如果没有提供 initialValue,那么第一次调用 callback 函数时,accumulator 使用原数组中的第一个元素,currentValue 即是数组中的第二个元素。 在没有初始值的空数组上调用 reduce 将报错。
  • 如果提供了 initialValue,那么将作为第一次调用 callback 函数时的第一个参数的值,即 accumulator,currentValue 使用原数组中的第一个元素。

来个简单的例子介绍下,现在有一个数组 [0, 1, 2, 3, 4],需要计算数组元素的和,需求比较简单,来看下代码实现。

不使用高阶函数

const arr = [0, 1, 2, 3, 4];
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += arr[i];
}
console.log( sum );
// 10
console.log( arr );
// [0, 1, 2, 3, 4]

使用高阶函数

无 initialValue 值

const arr = [0, 1, 2, 3, 4];
let sum = arr.reduce((accumulator, currentValue, currentIndex, array) => {
return accumulator + currentValue;
});
console.log( sum );
// 10
console.log( arr );
// [0, 1, 2, 3, 4]

上面是没有 initialValue 的情况,代码的执行过程如下,callback 总共调用四次。

callback accumulator currentValue currentIndex array return value
first call 0 1 1 [0, 1, 2, 3, 4] 1
second call 1 2 2 [0, 1, 2, 3, 4] 3
third call 3 3 3 [0, 1, 2, 3, 4] 6
fourth call 6 4 4 [0, 1, 2, 3, 4] 10

有 initialValue 值

我们再来看下有 initialValue 的情况,假设 initialValue 值为 10,我们看下代码。

const arr = [0, 1, 2, 3, 4];
let sum = arr.reduce((accumulator, currentValue, currentIndex, array) => {
return accumulator + currentValue;
}, 10);
console.log( sum );
// 20
console.log( arr );
// [0, 1, 2, 3, 4]

代码的执行过程如下所示,callback 总共调用五次。

callback accumulator currentValue currentIndex array return value
first call 10 0 0 [0, 1, 2, 3, 4] 10
second call 10 1 1 [0, 1, 2, 3, 4] 11
third call 11 2 2 [0, 1, 2, 3, 4] 13
fourth call 13 3 3 [0, 1, 2, 3, 4] 16
fifth call 16 4 4 [0, 1, 2, 3, 4] 20

函数作为返回值输出

这个很好理解,就是返回一个函数,下面直接看两个例子来加深理解。

isType 函数

我们知道在判断类型的时候可以通过 Object.prototype.toString.call 来获取对应对象返回的字符串,比如:

let isString = obj => Object.prototype.toString.call( obj ) === '[object String]';
let isArray = obj => Object.prototype.toString.call( obj ) === '[object Array]';
let isNumber = obj => Object.prototype.toString.call( obj ) === '[object Number]';

可以发现上面三行代码有很多重复代码,只需要把具体的类型抽离出来就可以封装成一个判断类型的方法了,代码如下。

let isType = type => obj => {
return Object.prototype.toString.call( obj ) === '[object ' + type + ']';
}
isType('String')('123'); // true
isType('Array')([1, 2, 3]); // true
isType('Number')(123); // true

这里就是一个高阶函数,因为 isType 函数将 obj => { ... } 这一函数作为返回值输出。

add 函数

我们看一个常见的面试题,用 JS 实现一个无限累加的函数 add,示例如下:

add(1); // 1
add(1)(2); // 3
add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10 
// 以此类推

我们可以看到结构和上面代码有些类似,都是将函数作为返回值输出,然后接收新的参数并进行计算。

我们知道打印函数时会自动调用 toString()方法,函数 add(a) 返回一个闭包 sum(b),函数 sum() 中累加计算 a = a + b,只需要重写sum.toString()方法返回变量 a 就可以了。

function add(a) {
function sum(b) { // 使用闭包
a = a + b; // 累加
return sum;
}
sum.toString = function() { // 重写toString()方法
return a;
}
return sum; // 返回一个函数
}
add(1); // 1
add(1)(2); // 3
add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript 打地鼠游戏代码说明
Oct 12 Javascript
javascript温习的一些笔记 基础常用知识小结
Jun 22 Javascript
基于jquery &amp; json的省市区联动代码
Jun 26 Javascript
给Flash加一个超链接(推荐使用透明层)兼容主流浏览器
Jun 09 Javascript
jquery中文乱码的多种解决方法
Jun 21 Javascript
Js参数值中含有单引号或双引号问题的解决方法
Nov 06 Javascript
javascript ajax的5种状态介绍
Aug 18 Javascript
JavaScript字符串对象toLowerCase方法入门实例(用于把字母转换为小写)
Oct 17 Javascript
vue图片加载与显示默认图片实例代码
Mar 16 Javascript
inner join 内联与left join 左联的实例代码
Sep 18 Javascript
AngularJS实现表单验证功能详解
Oct 12 Javascript
浅谈ES6 模板字符串的具体使用方法
Nov 07 Javascript
javascript防抖函数debounce详解
Jun 11 #Javascript
详解ng-alain动态表单SF表单项设置必填和正则校验
Jun 11 #Javascript
vue实现路由懒加载及组件懒加载的方式
Jun 11 #Javascript
Vue+element 解决浏览器自动填充记住的账号密码问题
Jun 11 #Javascript
Flutter部件内部状态管理小结之实现Vue的v-model功能
Jun 11 #Javascript
JavaScript动态检测密码强度原理及实现方法详解
Jun 11 #Javascript
聊聊Vue 中 title 的动态修改问题
Jun 11 #Javascript
You might like
php 论坛采集程序 模拟登陆,抓取页面 实现代码
2009/07/09 PHP
php下利用curl判断远程文件是否存在的实现代码
2011/10/08 PHP
ThinkPHP字符串函数及常用函数汇总
2014/07/18 PHP
PHP url的pathinfo模式加载不同控制器的简单实现
2016/08/12 PHP
centos下file_put_contents()无法写入文件的原因及解决方法
2017/04/01 PHP
JS中简单的实现像C#中using功能(有源码下载)
2007/01/09 Javascript
关于图片验证码设计的思考
2007/01/29 Javascript
使用IE的地址栏来辅助调试Web页脚本
2007/03/08 Javascript
Javascript实现CheckBox的全选与取消全选的代码
2010/07/20 Javascript
用方法封装javascript的new操作符(一)
2010/12/25 Javascript
jQuery实现动画效果的简单实例
2014/01/27 Javascript
jQuery中dequeue()方法用法实例
2014/12/29 Javascript
AngularJS基础知识笔记之表格
2015/05/10 Javascript
使用jquery动态加载Js文件和Css文件
2015/10/24 Javascript
浅谈jquery.form.js的ajaxSubmit和ajaxForm的使用
2016/09/09 Javascript
js正则表达式惰性匹配和贪婪匹配用法分析
2016/12/26 Javascript
vue实现某元素吸顶或固定位置显示(监听滚动事件)
2017/12/13 Javascript
了解JavaScript中的选择器
2019/05/24 Javascript
vue+ElementUI 关闭对话框清空验证,清除form表单的操作
2020/08/06 Javascript
python实现DNS正向查询、反向查询的例子
2014/04/25 Python
Python中使用strip()方法删除字符串中空格的教程
2015/05/20 Python
python thrift搭建服务端和客户端测试程序
2018/01/17 Python
Python 编码规范(Google Python Style Guide)
2018/05/05 Python
Django1.9 加载通过ImageField上传的图片方法
2018/05/25 Python
Kears 使用:通过回调函数保存最佳准确率下的模型操作
2020/06/17 Python
Soft Cotton捷克:来自爱琴海棉花的浴袍
2017/02/01 全球购物
Booking.com西班牙:全球酒店预订
2018/03/30 全球购物
Doyoueven官网:澳大利亚健身服饰和配饰品牌
2019/03/24 全球购物
进口业务员岗位职责
2014/04/06 职场文书
《傅雷家书》教学反思
2014/04/20 职场文书
小学五一劳动节活动总结
2015/02/09 职场文书
社区端午节活动总结
2015/02/11 职场文书
2016教师读书思廉心得体会
2016/01/23 职场文书
在前女友婚礼上,用Python破解了现场的WIFI还把名称改成了
2021/05/28 Python
使用 Apache Superset 可视化 ClickHouse 数据的两种方法
2021/07/07 Servers
Python实现视频中添加音频工具详解
2021/12/06 Python