经常遇到的问题

一个前端经常会遇到的问题
  • 例如,我想要在一个项目里,监听所有的fetch请求,应该怎么办?又或者说,我想用别人封装好的方法,但是在它之前,需要经过一层处理、判断,然后再看情况是否调用别人封装好的方法

  • 这种需求,大都是相似的,记得我之前讲解过koa洋葱圈、redux中间件等源码是我们前端的一种核心解决问题的思维方式

从如何监听fetch请求说起
  • 例如,fetch的使用,一般是:

    fetch(url).then((res)=>{...})

  • 那么对于一个已经使用过很多次的项目,我需要监听这个fetch请求。应该怎么办呢?很简单,我们"重写"fetch

    const myFetch = window.fetch;
    window.fetch = function (...arg) {
    console.log("此时调用了fetch,参数是:", arg);
    return myFetch(arguments);
    };
    fetch("www.baidu.com").then((res) => {
    console.log(res, "res");
    });

  • 简单的几行代码,就实现了监听所有的fetch请求。而且不会对原有的功能有入侵,不过问题来了

  • 这样做,其实相当于去修改原型链上的方法,导致纯净的原有方法被污染。真正操作时候,建议新写一个方法,然后全局替换即可,这样能保证底层的fetch方法是正常的,不会影响那些不需要log的地方,那么我们今天顺便讲讲类似的源码实现
到redux最精髓的中间件源码compose函数
  • 比如说我们有这样几个函数:

    function add1(str) {
    return str + 1
    }

    function add2(str) {
    return str + 2
    }

    function add3(str) {
    return str + 3
    }

  • 我们想依次执行函数,并把执行结果传到下一层就要像下面一样一层套一层的去写:

    let newstr = add3(add2(add1("abc"))) //"abc123"]

  • 这只是3个,如果数量多了或者数量不固定处理起来就很麻烦,但是我们用compose写起来就很优雅:

    let newaddfun = compose(add3, add2, add1);
    let newstr = newaddfun("abc") //"abc123"

那compose内部是如何实现的呢?
复制代码
function compose(...funcs) {
    return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
  • 其实核心代码就一句,这句代码使用了reduce方法巧妙地将一系列函数转为了add3(add2(add1(...args)))这种形式,我们使用上面的例子一步一步地拆分看一下

  • 当调用compose(add3, add2, add1)funcs是add3, add2, add1,第一次进入时a是add3b是add2,展开就是这样子:(add3, add2)=>(...args)=>add3(add2(...args)),传入了add3, add2,返回一个这样的函数(...args)=>add3(add2(...args)),然后reduce继续进行,第二次进入时a是上一步返回的函数(...args)=>add3(add2(...args))b是add1,于是执行到a(b(...args)))时,b(...args)作为a函数的参数传入,变成了这种形式:(...args)=>add3(add2(add1(...args))),是不是很巧妙。

如果你这里理解不了,你今天不睡觉也要搞明白,拿纸去写下来实现调用过程,这就是前端里面最硬的干货了。

再到promise链式调用实现
  • Promise链式调用跟JQ的区别在于,promise.then每次返回的都是一个新的promise实例对象,这样实现了链式调用。

  • JQ链式,相当于一个函数内部永远都返回当前这个相同的this

    //这里我也忘了是不是这样用了
    $('#root').style(...).style(...)

  • Promise链式,最后打印结果是Peter老师666,每次.then返回的是一个新的Promise

    Promise.resolve("Peter老师666")
    .then((res) => {
    return res;
    })
    .then((res) => {
    console.log(res,'res')
    return res;
    });

再到express的next
  • 当我们在路由处理中调用next()时候,就会进入下一个匹配,例如当我们用get请求接口test时候,就会走到console.log("此时被触发")这行代码

    app.get('/test',(req,res,next)=>{
    next()

    })
    app.get('*',(req,res,next)=>{
    console.log("此时被触发")
    })

  • 原因是什么?实现express的next很简单

    复制代码
    handle(req, res, matchedList) {
      const next = () => {
        const midlleware = matchedList.shift();
        if (midlleware) {
          midlleware(req, res, next);
        }
      };
      next();
    }
  • 我们先找到所有匹配的请求方式和路由,然后首先处理第一个匹配中的路由,使用闭包方式传入next方法。当调用next方法时候,会执行下一个匹配中的路由回调函数。这样就实现了next

再到koa的中间件
  • 怎么使用koa的中间件?

    app.use(middlewaer1).use(middlewaer2).use(middlewaer3)

  • 是不是感觉跟JQ的链式调用很像,其实一样,都是统一返回this即可。如下所示:

    const obj = {
    print(arg) {
    console.log(arg);
    return this;
    },
    hello(arg) {
    console.log(arg);
    return this;
    },
    world(arg) {
    console.log(arg);
    return this;
    },
    };
    obj.print('Peter').print('hello').print('world');

  • 此时控制台就输出了Peter,hello,world。我们实现了最简单的链式调用

相关推荐
腾讯TNTWeb前端团队5 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰8 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪9 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪9 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy9 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom10 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom10 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom10 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom10 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom10 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试