Javascript--高阶函数之从实践中感悟(封装new,forEach,map)

高阶函数?听着很高级的样子,本节我们从实践入手,来封装一个自己的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)
  • 除此之外,高阶函数还与柯里化有着紧密的联系,我将在下一篇文章中重点聊聊柯里化

总结

  • 高阶函数的目的是为了增强一个函数的功能;
  • 高阶函数需要一个函数作为参数,函数可以视为'一等公民';
  • 很多地方都会用到高阶函数,需要我们不断积累总结,扩展思维;
相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端