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

总结

  • 高阶函数的目的是为了增强一个函数的功能;
  • 高阶函数需要一个函数作为参数,函数可以视为'一等公民';
  • 很多地方都会用到高阶函数,需要我们不断积累总结,扩展思维;
相关推荐
Python私教几秒前
Go语言现代web开发15 Mutex 互斥锁
开发语言·前端·golang
A阳俊yi1 分钟前
Vue(13)——router-link
前端·javascript·vue.js
好看资源平台14 分钟前
前端框架对比与选择:如何在现代Web开发中做出最佳决策
前端·前端框架
4triumph17 分钟前
Vue.js教程笔记
前端·vue.js
程序员大金33 分钟前
基于SSM+Vue+MySQL的酒店管理系统
前端·vue.js·后端·mysql·spring·tomcat·mybatis
清灵xmf36 分钟前
提前解锁 Vue 3.5 的新特性
前端·javascript·vue.js·vue3.5
Jiaberrr42 分钟前
教你如何在微信小程序中轻松实现人脸识别功能
javascript·微信小程序·小程序·人脸识别·百度ai
白云~️1 小时前
监听html元素是否被删除,删除之后重新生成被删除的元素
前端·javascript·html
2401_864476931 小时前
无线领夹麦克风哪个降噪好?一文搞懂麦克风什么牌子的音质效果好
javascript·git·sql·github·mssql
金灰1 小时前
有关JS下隐藏的敏感信息
前端·网络·安全