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

总结

  • 高阶函数的目的是为了增强一个函数的功能;
  • 高阶函数需要一个函数作为参数,函数可以视为'一等公民';
  • 很多地方都会用到高阶函数,需要我们不断积累总结,扩展思维;
相关推荐
nothing_more_than2 小时前
draggable的el-dialog实现对话框标题可以选择
javascript·vue.js·element-plus
小镇程序员3 小时前
vue2 src自定义事件
前端·javascript·vue.js
炒毛豆3 小时前
vue3+echarts+ant design vue实现进度环形图
javascript·vue.js·echarts
AlgorithmAce5 小时前
Live2D嵌入前端页面
前端
nameofworld5 小时前
前端面试笔试(六)
前端·javascript·面试·学习方法·递归回溯
前端fighter5 小时前
js基本数据新增的Symbol到底是啥呢?
前端·javascript·面试
流着口水看上帝6 小时前
JavaScript完整原型链
开发语言·javascript·原型模式
guokanglun6 小时前
JavaScript数据类型判断之Object.prototype.toString.call() 的详解
开发语言·javascript·原型模式
GISer_Jing6 小时前
从0开始分享一个React项目:React-ant-admin
前端·react.js·前端框架
川石教育6 小时前
Vue前端开发子组件向父组件传参
前端·vue.js·前端开发·vue前端开发·vue组件传参