目录

一、高阶函数
什么是JS高阶函数?JavaScript中的高阶函数(Higher-Order Function,HOF) 是指接受其他函数作为参数、返回一个函数或同时满足上述两种条件的函数。这类函数通过传递函数实现代码复用,提升代码的可读性和维护性。
在JS的内置对象中同样存在着一些高阶函数,像数组的map,filter,reduce方法等,它们接受一个函数作为参数,并应用这个函数到列表的每一个元素。
1.1 map
map() 方法是数组原型的一个函数,该函数用于对数组中的每个元素进行处理,将其转换为另一个值,最终返回一个新的数组,该数组包含了经过处理后的每个元素。
map方法接收一个函数作为参数 ,遍历数组,并且返回一个新的数组,新的数组里的每个元素都执行map传入的函数。
map() 方法的基本语法:
array.map(callback(currentValue[, index[, array]])[, thisArg])
其中:
callback :表示对数组中的每个元素要执行的函数。该函数包含三个参数:
- currentValue:表示正在处理的当前元素。
- index:可选参数,表示正在处理的当前元素的索引。
- array:可选参数,表示正在处理的当前数组。
thisArg:可选参数,表示执行 callback 函数时的 this 值。
例子:
let arr = [1, 2, 3, 4];
let arr1 = arr.map(item => item * 2)
console.log(arr1);// [2, 4, 6, 8]
返回的是一个新数组arr1,不改变原数组 。注意:如果传入的参数没有返回值,则数组的每一项都会是undefind 。
经典题目
console.log(['1','2','3'].map(parseInt));
来看看上面这个代码输出什么?
答案:[1, NaN, NaN]
▼解析
parseInt() 函数可解析一个字符串,并返回一个整数。
当参数 radix 的值为 0,或没有设置该参数时,parseInt()会根据该字符串来判断数字的基数。
当忽略参数 radix ,默认的基数如下:
❶如果 字符串 以 “0x” 开头,parseInt() 会把其余部分解析为十六进制的整数。
❷如果字符串以 0 开头,把其余部分解析为八进制或十六进制的数字。
❸如果字符串以 1 ~ 9 的数字开头,parseInt()将把它解析为十进制的整数
注意:基数可不是默认十进制噢!
当我们把数组传入parseInt时,由于接收2个参数,会将数组的索引作为基数传给parseInt,所以实质上进行的是以下几步:
// 当参数为 0,parseInt() 会根据十进制来解析.输出结果:1
parseInt('1', 0)
// 没有1对应的匹配规则,无法正确处理,输出结果:NaN
parseInt('2', 1)
// 以二进制来解析字符串,但是3并不是以0或1为开头的二进制格式。输出结果: NaN
parseInt('3', 2)
也就是说parseInt(‘1’, 0),第一个是字符串,第二个参数是多少进制,0是10进制,所以十进制的字符串’1’就是1;——
parseInt(‘2’, 1),1进制会有2吗,不会,所以是NaN;
parseInt(‘3’, 2),2进制也不会出现3,所以也是NaN。
注意:如果字符串的第一个字符不能被转换为数字,那么parseInt()会返回 NaN。
✔小tips:▼
parseInt()函数的用法与注意事项
parseInt()是JavaScript中一个常用的全局函数,用于将字符串转换为整数。虽然这个函数看起来简单,但在实际使用中有许多需要注意的细节。
parseInt()函数的基本语法是:
parseInt(string, radix);
● string:必需参数,表示要被解析的字符串。如果参数不是一个字符串,则会先将其转换为字符串。
● radix:可选参数,表示要解析的数字的基数(进制),取值范围是2到36之间的整数(也就是多少进制)。
▶注:radix:解析字符串的基数,基数规则如下:
从 2 到 36 的整数,表示进制的基数。例如,指定 16 表示被解析值是十六进制数。如果超出这个范围,将返回 NaN。假如指定 0 或未指定,基数将会根据字符串的值进行推算。 ————来自MDN。
1.2 filter
一般来说,filter() 方法用于过滤数组中的元素,并返回一个新数组。它接收一个函数作为参数,通过这个函数来指定筛选数组的规则,最后返回满足规则的新数组。
array.filter(function(value, index, arr), thisValue)
▼参数说明
function:函数,规定了过滤条件。必需。该函数接受三个参数:当前元素的值、当前元素的索引和包含该元素的数组。
- value 必须。当前元素的值
- index 可选。当前元素的索引值
- arr 可选。当前元素属于的数组对象
thisValue:可选项。对象作为该执行回调时使用,传递给函数,用作 “this” 的值。如果省略thisValue,则默认是全局对象(浏览器环境下是 window 对象)。
▼返回值
返回一个新数组,其中包含符合条件的元素。看懂例子,基本就掌握了。
注意:不会检测空数组,不会改变原始数组
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let arr1 = arr.filter(num => {
return num > 5
})
console.log(arr1);// [6, 7, 8, 9]
1.3 reduce
reduce能做的事情很多,但是我们平时都使用for循环之类的方法代替了,但是reduce真的高逼格
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
在w3school中给出的reduce语法,这里我们常用的只有前面两个
▼参数描述
❶ function 函数(执行数组中每个值的函数,包含四个参数):
● total 必需。初始值,或者计算结束后的返回值。
上一次调用回调返回的值,或者是提供的初始值(initialValue)
● currentValue 必需。当前元素——数组中当前被处理的元素
● currentIndex 可选。当前元素在数组中的索引
● arr 可选。调用 reduce 的数组
❷ initialValue 可选 。表示初始值,作为第一次调用 function 的第一个参数。
提供初始值,currentValue 从数组第一项开始,若不提供初始值,则 currentValue 从第二项开始执行,对应的第一次 total 是数组第一项的值。
例子:
let arr = [1, 2, 3, 4]
let sum = arr.reduce((value, item) => {
console.log(value, item);
// 1 2 3 3 6 4
return value + item
})
console.log(sum);// 10
从第四行的调试中可以看出reduce函数的执行过程,在没有初始值的情况下,将数组第一个值作为value,第二个值作为item再依次往下遍历整个数组,将返回值作为value,数组的下一位作为item,直至遍历完成。

利用ruduce实现数组去重
let arr = [1,1,2,3,4,2,5,4];
let unique = arr.reduce(function (prev, item) {
prev.indexOf(item) === -1 && prev.push(item);
return prev;
}, []);
console.log(unique); // [1, 2, 3, 4, 5]
通过将空数组作为prev初始值,再通过indexOf判断数组中是否包含item,如果没有就将item加入数组,最终返回数组。关于&&运算符,第一条语句为true则执行第二条,否则不执行。
ruduce的用法远不止这些,有兴趣的大家可以再深入了解一下。
还有很多内置对象都是高阶函数,例如Function.prototype.bind()、 setTimeout() 和 setInterval() 的回调函数也是高阶函数的例子,这里就不一一说明了,从上面的三个方法中,已经能很直观的感受到了函数接收函数作为参数,再返回值的过程,逼格很高也很好用。
▶番外:其他高阶函数的例子
1、Function.prototype.bind()
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,其余参数将作为新函数的参数,供调用时使用。
function greet() {
console.log("Hello, " + this.name);
}
const person = { name: "Alice" };
const greetAlice = greet.bind(person);
greetAlice(); // 输出: Hello, Alice
2、setTimeout() 和 setInterval()
setTimeout(function() {
console.log("This is a delayed message.");
}, 1000); // 1秒后输出: This is a delayed message.
高阶函数是JavaScript中强大的工具,它们使得代码更加简洁和可维护。通过使用高阶函数,你可以避免重复代码,并利用函数式编程范式中的一些强大特性。
二.、AOP 面向切面编程
当我们需要使用一个公共函数,并且需要在这个函数执行前后添加自己的逻辑,通常我们的做法不能是直接修改这个函数,因为它是公共函数,这时候我们可以通过AOP的方法利用高阶函数和原型链的特点进行处理。
把一些与业务无关的功能抽离出来,通过"动态植入"的方法,掺入到业务逻辑模块中。这样做的好处是保证业务逻辑模块的纯净和高内聚,其次可以方便的复用功能模块。
下面我们要实现在函数执行前输出提示信息
function say(who) {
console.log(who + ':函数执行了');
}
Function.prototype.before = function(callback) {
return (...args) => {
callback()
this(...args) // 类型名.prototype.共有方法=function(){ ... } this->将来谁调用指谁
}
}
let whoSay = say.before(function() {
console.log('你要被调用了');
})
whoSay('suny')
// 你要被调用了
// suny:函数执行了
如果需要实现后置通知,只需要将6,7行换以下就可以了。
实现的原理
在调用公共函数时,传入我们需要执行提前执行的函数,在内部函数执行前先调用该函数。
三、偏函数
当一个函数有很多参数时,调用该函数就需要提供多个参数,如果可以减少参数的个数,就能简化该函数的调用,降低调用该函数的难度。
实现3个数求和
function sum(a, b, c){
return a + b + c;
}
sum(1, 2, 3) // 6
在调用时我们需要传入3个参数,好像有些许麻烦,下面我们用偏函数的做法 创建一个新的partial函数,这个新函数可以固定住原函数的部分参数,从而减少调用时的输入的参数,让我们的调用更加简单。
function sum(a, b, c) {
return a + b + c
}
function partial(sum, c) {
return function (a, b) {
return sum(a, b, c)
}
}
let partialSum = partial(sum, 3)// -> 6
高阶函数除了可以接收函数作为参数外,还可以将函数作为结果返回,偏函数就是固定了函数的一个或多个参数,返回一个新的函数接收剩下的参数,以此来简化函数的调用。
Function.prototype.bind 函数就是一个偏函数的典型代表,它接受的第二个参数开始,为预先添加到绑定函数的参数列表中的参数。
四、函数柯里化
与偏函数不同,柯里化是把接收多个参数的函数转换成多个只接收一个参数的函数。
我们从一个简单的例子来认识函数柯里化
function add(a, b) {
return a + b;
}
add(1, 2) // 3 普通做法,一次传入两个参数
// 假设有一个 curring 函数可以做到柯里化
function curring(){}
curring(1)(2) // 我们通过这样的方式来接受参数,这样就实现了柯里化
接下来我们来看看利用柯里化来实现
function curring(x) {
return y => x + y
}
curring(1)(2) // => 3
4.1 函数柯里化的作用
要真正理解柯里化还是得看示例。
4.1.1 参数复用
我们先看一段短短的代码,这段代码中,实现了输入输出个人信息的功能,通过myInfo函数将参数拼接返回,这实际上很简单,但是当用很多很多的用户信息时,需要一直传递着个人信息这个参数,这样显然是不合理的。
function myInfo(inf, name, age) {
return `${inf}:${name}${age}`
}
const myInfo1 = myInfo('个人信息', 'ljc', '19')
console.log(myInfo1); // 个人信息:ljc19
下面我们通过柯里化技术来解决
function myInfoCurry(inf) {
return (name, age) => {
return `${inf}:${name}${age}`
}
}
let myInfoName = myInfoCurry('个人信息')
const myInfo1 = myInfoName('ljc', '19')
const myInfo2 = myInfoName('ljcc','19')
console.log(myInfo2); // 个人信息:ljcc119
console.log(myInfo1); // 个人信息:ljc19
参数复用就是柯里化技术的作用之一了,在上面代码的基础上,我们可以继续扩展我们的信息,就像这样,利用一个函数就可以实现多个功能。
let myInfoSex = myInfoCurry('爱好')
const myInfo3 = myInfoSex('看球赛','听歌')
console.log(myInfo3); // 爱好:看球赛听歌
4.1.2 提前返回
这个特性是用来对浏览器的监听事件兼容性做一些判断并初始化,解决有些浏览器对addEventListener存在的兼容性问题,所以在使用之前做一次判断,之后就可以省略了
const whichEvent = (function () {
if (window.addEventListener) {
return function (ele, type, listener, useCapture) {
ele.addEventListener(type, function (e) {
listener.call(ele, e)
}, useCapture)
}
} else if (window.attachEvent) {
return function (ele, type, handler) {
ele.attachEvent('on' + type, function (e) {
handler.call(ele, e)
})
}
}
})()
由于使用了立即执行函数,即使触发多次事件依旧只会触发一次if条件判断。
▶番外:关于事件监听对象 addEventListener
addEventListener的三个参数分别是:事件类型(type,字符串)、事件处理函数(listener,函数)和可选的配置对象(options,布尔值或对象),用于控制事件监听行为。
✔参数详解
1、type(事件类型)。
- 类型:字符串。
- 用途:指定监听的事件名称(如
"click"、"mouseover"),无需添加"on"前缀。例如:"click"对应点击事件。
2、listener(事件处理函数)。
- 类型:函数。
- 用途:事件触发时执行的回调函数,接收一个
Event对象作为参数。可以是匿名函数或具名函数引用。
3、options(配置项)。
- 类型:布尔值或对象。
- 用途:
❶布尔值:若为true,事件在捕获阶段触发;默认为false(冒泡阶段触发)。
❷对象:支持以下属性(ECMAScript扩展):
◆capture:同布尔值功能,控制事件阶段。
◆once:若为true,监听器仅执行一次后自动移除。
◆passive:若为true,表示监听器不会调用preventDefault(),优化滚动性能。
✔函数作用
addEventListener用于为DOM元素动态绑定事件监听器,支持同一事件的多个处理函数,且可通过配置精确控制事件触发阶段(捕获/冒泡)和生命周期。
4.1.3 延迟执行
下面我们通过一道例题来了解——编写一个add函数实现下面的功能。
add(1)(2)(3) = 6
add(1, 2, 3)(4) = 10
add(1)(2)(3)(4)(5) = 15
function add(...args) {
let inner = function () {
args.push(...arguments);
inner.toString = function () {
return args.reduce((prev, cur) => {
return prev + cur
})
}
return inner
}
return inner
}
console.log(add(1)(2)(3)); // f 6
这是一个JavaScript的柯里化(currying)函数实现,它实现了以下功能:
函数功能:
- 通过闭包特性实现连续调用累加
- 每次调用都会将参数保存到args数组中
- 最终通过toString隐式转换返回累加结果
执行过程:
- add(1) 返回inner函数并保存1到args
- (2) 将2追加到args并返回inner
- (3) 将3追加到args并返回inner
- 当需要字符串输出时,触发toString计算总和
输出结果:
- 最终console.log会隐式调用toString方法
- 输出1+2+3=6的结果
这个实现展示了JavaScript中:
- 闭包的应用
- 函数柯里化
- 隐式类型转换
- 链式调用等特性
注意:实际使用时建议显式调用toString()或valueOf()方法获取结果,而不是依赖隐式转换。
这段代码中涵盖的知识面很多,核心的部分在于inner.toString这里,利用了当返回一个函数时返回的是它的字符串形式,所以我们可以利用这个特性来自定义我们的返回值。
▶ 参考:什么是函数柯里化?函数柯里化详解
✔参考资料▼
JS数组高阶函数——map()方法 | JavaScript map()方法超全介绍 | JS中map函数的实用技巧总结
JS数组高阶函数——filter()方法 | JS中的filter() 函数 | filter方法 · JavaScript对象详解 · 看云
reduce方法 · JavaScript对象详解 · 看云 | JS高阶函数——reduce()用法详解总结
JavaScript中filter、map、reduce、find和findIndex函数的基本使用


被折叠的 条评论
为什么被折叠?



