高阶函数?听着很高级的样子,本节我们从实践入手,来封装一个自己的new forEach map
方法,然后再来感悟一下高阶函数!
先忘记我们的主题,接下来将关注点放在下面的封装Demo上吧!🎴
封装Demo
new
我们知道,new
操作符可以帮助我们创建一个'类'的实例,而在JS中,一个函数就可以是一个类,new
关键字帮我们创建了一个类的实例对象,并维护好了其原型。
除此之外,在面向对象编程中,有个很关键的点就是this
,我们知道this的指向是在函数运行时确定的,是动态的 ,因此我们还需要在创建新的对象时维护好this
;
我们期望的调用方式:const ys = _new(Person,'ys_OoO')
实现:
js
/**
* @param {Function} func
*/
function _new(func) {
//创建实例对象,初始为空
let res = {};
//维护原型&原型链
if (func.prototype !== null) {
res.__proto__ = func.prototype;
} else {
throw new TypeError("First param should be a constructor");
}
//绑定this为res,并将参数传递给func并调用
const ret = func.apply(res, Array.from(arguments).slice(1))
//如果构造函数有返回值,那么返回
if (ret) {
return ret;
}
return res;
}
测试一下:
js
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = () => {
console.log('my name is ' + this.name)
}
const person = _new(Person, 'ys', 24);
person.say() // my name is ys
map
在封装map之前,你得先了解一下map的算法设计,在这篇文章中详细介绍了,强烈推荐你读一下🤗!
我们期望的调用方式:
js
const array = [1, 2, 3];
//第一个参数:回调函数 第二个参数:回调函数中this的指向
//回调函数的三个参数为:当前循环的项,当前循环的下标,当前被遍历对象
array._map(function(item,index,array){
console.log(item,index,array,this);
return item+1;
},person)
实现:
js
/**
* @param {function} func 每次循环的回调函数
* @param {*} cb_this 回到函数中的this
*/
Array.prototype._map = function (func, cb_this) {
//循环次数
const length = this.length;
if (typeof func !== 'function') {
throw new TypeError("First param should be a function")
}
//初始化结果
let res = new Array(length).fill(undefined);
let k = 0;
while (k < length) {
//调用回调,将内部this指向thisArg,并传递所需参数
res[k] = func.apply(cb_this, [this[k], k, this])
k++;
}
return res;
}
测试一下:
forEach
同样,在封装forEach之前,你得先了解一下forEach的算法设计,在这篇文章中详细介绍了,强烈推荐你读一下🤗!
我们期望的调用方式:
js
const array = [1, 2, 3];
//第一个参数:回调函数 第二个参数:回调函数中this的指向
//回调函数的三个参数为:当前循环的项,当前循环的下标,当前被遍历对象
array.forEach(function(item,index,array){
console.log(item,index,array,this);
},person)
实际上forEach的实现与map大同小异,不再需要返回值了,直接看代码:
js
/**
* @param {function} func 每次循环的回调函数
* @param {*} cb_this 回到函数中的this
*/
Array.prototype._forEach = function (func, cb_this) {
//循环次数
const length = this.length;
if (typeof func !== 'function') {
throw new TypeError("First param should be a function")
}
let k = 0;
while (k < length) {
//调用回调,将内部this指向thisArg,并传递所需参数
func.apply(cb_this, [this[k], k, this])
k++;
}
}
高阶函数
回归主题,接下来我就要聊聊什么是高阶函数了!😎
例如我们上面写到的例子,封装一个forEach、map,我们都知道需要使用循环来实现,无论是while
还是for
。而我们实现的函数就是在增强 循环,为循环这个功能添加其他功能;
除此之外,高阶函数还有一个特点就是:需要传入一个函数作为参数;
高阶函数并没有那么高级,但是他真的很有用!
案例
高阶函数在日常开发中能干什么?
- 在对不同用户展示不同页面时,我们需要获取用户权限,那么我们可以将
获取用户权限的函数
进行封装,这样就可以方便的使用它了 - 例如我在一文吃透👉大文件分片上传、断点续传、秒传⏫这篇文章中我们需要在文件分片上传时获取文件上传进度,我们可以传递一个
progress_cb
来将上传进度暴露给外部使用 - 还有一个更加实用的,我们知道React现在都是函数式组件,也就是说一个组件就是一个函数,我们可以将组件进行封装,增强 组件的功能;可以看看我这篇文章中的Demo:React 高阶组件HOC(Higher Order Component)
- 除此之外,高阶函数还与柯里化有着紧密的联系,我将在下一篇文章中重点聊聊柯里化
总结
- 高阶函数的目的是为了增强一个函数的功能;
- 高阶函数需要一个函数作为参数,函数可以视为'一等公民';
- 很多地方都会用到高阶函数,需要我们不断积累总结,扩展思维;