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

总结

  • 高阶函数的目的是为了增强一个函数的功能;
  • 高阶函数需要一个函数作为参数,函数可以视为'一等公民';
  • 很多地方都会用到高阶函数,需要我们不断积累总结,扩展思维;
相关推荐
VT.馒头5 分钟前
【力扣】2625. 扁平化嵌套数组
前端·javascript·算法·leetcode·职场和发展·typescript
cyforkk8 分钟前
09、Java 基础硬核复习:异常处理(容错机制)的核心逻辑与面试考点
java·数据库·面试
数研小生34 分钟前
Full Analysis of Taobao Item Detail API taobao.item.get
java·服务器·前端
Shirley~~43 分钟前
Vue-skills的中文文档
前端·人工智能
毎天要喝八杯水1 小时前
搭建vue前端后端环境
前端·javascript·vue.js
计算机程序设计小李同学1 小时前
幼儿园信息管理系统的设计与实现
前端·bootstrap·html·毕业设计
雨季6661 小时前
Flutter 三端应用实战:OpenHarmony “极简手势轨迹球”——指尖与屏幕的诗意对话
开发语言·javascript·flutter
雨季6662 小时前
Flutter 三端应用实战:OpenHarmony “专注时光盒”——在碎片洪流中守护心流的数字容器
开发语言·前端·安全·flutter·交互
tao3556672 小时前
【用AI学前端】HTML-02-HTML 常用标签(基础)
前端·html
2601_949532842 小时前
Psello HTML Template: A Developer‘s Deep-Dive Review and Guide - Download Free
前端·windows·html·seo·wordpress·gpl