深入学习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 相关文章推荐
学习ExtJS 访问容器对象
Oct 07 Javascript
深入了解Node.js中的一些特性
Sep 25 Javascript
jQuery中fadeOut()方法用法实例
Dec 24 Javascript
JQuery动态添加和删除表格行的方法
Mar 09 Javascript
Javascript设计模式理论与编程实战之简单工厂模式
Nov 03 Javascript
跟我学习javascript的arguments对象
Nov 16 Javascript
jquery通过扩展select控件实现支持enter或focus选择的方法
Nov 19 Javascript
深入浅析JavaScript字符串操作方法 slice、substr、substring及其IE兼容性
Dec 16 Javascript
微信小程序自定义对话框弹出和隐藏动画
Jul 19 Javascript
微信小程序开发之tabbar图标和颜色的实现
Oct 17 Javascript
vue 项目 iOS WKWebView 加载
Apr 17 Javascript
原生JS与JQ获取元素的区别详解
Feb 13 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 shell超强免杀、减少体积工具实现代码
2012/10/16 PHP
PHP上传图片类显示缩略图功能
2016/06/30 PHP
ajax+php实现无刷新验证手机号的实例
2017/12/22 PHP
如何优雅的使用 laravel 的 validator验证方法
2018/11/11 PHP
Aster vs KG BO3 第一场2.18
2021/03/10 DOTA
document.getElementById的简写方式(获取id对象的简略写法)
2010/09/10 Javascript
基于jQuery的输入框在光标位置插入内容, 并选中
2011/10/29 Javascript
window.returnValue使用方法示例介绍
2014/07/03 Javascript
浅谈javascript面向对象程序设计
2015/01/21 Javascript
javascript实现tab切换的两个实例
2015/11/05 Javascript
JavaScript如何实现在文本框(密码框)输入提示语
2015/12/25 Javascript
详解nodejs操作mongodb数据库封装DB类
2017/04/10 NodeJs
js原生代码实现轮播图的实例讲解
2017/07/28 Javascript
react路由配置方式详解
2017/08/07 Javascript
基于es6三点运算符的使用方法(实例讲解)
2017/10/12 Javascript
jQuery 实现左右两侧菜单添加、移除功能
2018/01/02 jQuery
Vue2.0 实现移动端图片上传功能
2018/05/30 Javascript
简化vuex的状态管理方案的方法
2018/06/02 Javascript
jquery图片预览插件实现方法详解
2019/07/18 jQuery
基于python log取对数详解
2018/06/08 Python
Python3.6使用tesseract-ocr的正确方法
2018/10/17 Python
Python的Tkinter点击按钮触发事件的例子
2019/07/19 Python
Python 字符串类型列表转换成真正列表类型过程解析
2019/08/26 Python
python能自学吗
2020/06/18 Python
canvas使用注意点总结
2013/07/19 HTML / CSS
html svg生成环形进度条的实现方法
2019/09/23 HTML / CSS
乌克兰香水和化妆品网站:Notino.ua
2018/03/26 全球购物
法国二手MacBook销售网站:Okamac
2019/03/18 全球购物
初二政治教学反思
2014/01/12 职场文书
消防标语大全
2014/06/07 职场文书
建筑安全生产责任书
2014/07/22 职场文书
趣味运动会加油词
2015/07/18 职场文书
婚宴新娘致辞
2015/07/28 职场文书
公司酒会致辞
2015/07/30 职场文书
Python面向对象之内置函数相关知识总结
2021/06/24 Python
收音机爱好者玩机13年,简评其使用过的19台收音机
2022/04/30 无线电