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)
  • 除此之外,高阶函数还与柯里化有着紧密的联系,我将在下一篇文章中重点聊聊柯里化

总结

  • 高阶函数的目的是为了增强一个函数的功能;
  • 高阶函数需要一个函数作为参数,函数可以视为'一等公民';
  • 很多地方都会用到高阶函数,需要我们不断积累总结,扩展思维;
相关推荐
来一碗刘肉面15 分钟前
React - ajax 配置代理
前端·react.js·ajax
bin915325 分钟前
DeepSeek 助力 Vue 开发:打造丝滑的二维码生成(QR Code)
前端·javascript·vue.js·ecmascript·deepseek
@LitterFisher29 分钟前
Excell 代码处理
前端·javascript·excel
十八朵郁金香2 小时前
【JavaScript】深入理解模块化
开发语言·javascript·ecmascript
m0_748230942 小时前
Redis 通用命令
前端·redis·bootstrap
一个 00 后的码农2 小时前
25林业研究生复试面试问题汇总 林业专业知识问题很全! 林业复试全流程攻略 林业考研复试真题汇总
考研·面试·面试问题·考研复试·考研调剂·面试真题·林业考研
YaHuiLiang2 小时前
一切的根本都是前端“娱乐圈化”
前端·javascript·代码规范
菜鸟一枚在这3 小时前
深入解析设计模式之单例模式
开发语言·javascript·单例模式
ObjectX前端实验室3 小时前
个人网站开发记录-引流公众号 & 谷歌分析 & 谷歌广告 & GTM
前端·程序员·开源
CL_IN4 小时前
企业数据集成:实现高效调拨出库自动化
java·前端·自动化