前端面试题-手撕代码题

news2024/9/21 14:37:51

1. 实现 Promise.all,或者 实现 Promise.allSettled

(1)promise.all:

传入一个promise数组,其中所有promise都变为成功状态,返回一个数组,数组内是各个promise的返回;若任意传入的promise变为拒绝状态,那么返回该拒绝状态的promise的返回。
思路:
输入是一个数组,这个数组中的元素一般都是一个 Promise 实例。
输出是一个新的 Promise 实例, resolve 的内容是一个数组,这个数组就是输入中每个 Promise 的 resolve 的值。
如果有一个 Promise 执行失败,那么返回的新的 Promise 也会 reject。
其次,Promise.all 是 Promise 类上的通用方法,而不是原型上的方法。所以其实现不需要挂在 prototype 上。

    <script>
      // 1. promise.all
      // 输入:[promise实例,promise实例,……]
      // 输出:全部成功resolve返回promise结果组成的数组,只要有一个reject返回失败的promise结果
      // 是promise类上通用方法,非原型方法,无需挂载在prototype上
      Promise.myAll = function (promises) {
        if (!Array.isArray(promises)) {
          console.error("must be a array");
          return;
        }
        const res = new Promise((resolve, reject) => {
          const data = [];
          // 成功执行的promise数
          let completedCnt = 0;
          // 循环执行每个promise
          promises.forEach((promise, index) => {
            // 确保处理的对象是一个Promise
            Promise.resolve(promise)
              .then((res) => {
                // promise执行后放入data中
                data[index] = res;
                completedCnt++;
                if (completedCnt === promises.length) {
                  resolve(data);
                }
              })
              .catch((err) => reject(err));
          });
        });
        return res;
      };

	  // 编写测试用例
      Promise.myAll([
        Promise.resolve(1),
        new Promise((resolve) => {
          setTimeout(() => {
            resolve(2);
          }, 1000);
        }),
        new Promise((resolve) => {
          setTimeout(() => {
            resolve(3);
          }, 2000);
        }),
      ])
        .then((res) => {
          console.log(res);
        })
        .catch();

      Promise.myAll([Promise.resolve(2), Promise.reject(new Error("Failed"))])
        .then((res) => {
          console.log(res);
        })
        .catch(console.err);
    </script>

在这里插入图片描述

以下图解均来自知乎栗子前端

在这里插入图片描述

(2)promise.allSettled :

传入一个promise数组,当所有promise都变成已完成状态时,返回一个数组,包含每个promise返回结果(状态、值或者拒绝原因)
在这里插入图片描述

      // 2. promise.allSettled
      // 输入:[promise实例,promise实例,……]
      // 输出:返回所有promise实例的结果
      // 是promise类上通用方法,非原型方法,无需挂载在prototype上
      Promise.myAllSettled = function (promises) {
        if (!Array.isArray(promises)) {
          console.error("must be a array");
          return;
        }
        const res = new Promise((resolve) => {
          const data = [];
          // 存放所有执行的promise结果
          let completedCnt = 0;
          // 循环执行每个promise
          promises.forEach((promise, index) => {
            // 确保处理的对象是一个Promise
            Promise.resolve(promise)
              .then((res) => {
                // promise执行后放入data中
                data[index] = { status: "fulfilled", value: res };
              })
              .catch((reason) => {
                data[index] = { status: "rejected", value: reason };
              })
              .finally(() => {
                completedCnt++;
                if (completedCnt === promises.length) {
                  resolve(data);
                }
              });
          });
        });
        return res;
      };

      // 测试用例
      Promise.myAllSettled([
        Promise.resolve(1),
        new Promise((resolve) => {
          setTimeout(() => {
            resolve(2);
          }, 1000);
        }),
        new Promise((resolve) => {
          setTimeout(() => {
            resolve(3);
          }, 2000);
        }),
        Promise.reject(new Error("Failed")),
      ])
        .then((res) => {
          console.log(res);
        })
        .catch(console.err);

在这里插入图片描述

(3)promise.race:

promise.race()接收一个包含过个promise实例,返回最先返回结果的promise(不论成功还是失败)
在这里插入图片描述

      // 3. promise.race
      // 输入:[promise实例,promise实例,……]
      // 输出:返回最先返回结果的promise
      // 是promise类上通用方法,非原型方法,无需挂载在prototype上
      Promise.myRace = function (promises) {
        if (!Array.isArray(promises)) {
          console.error("must be a array");
          return;
        }
        const res = new Promise((resolve, reject) => {
          // 循环执行每个promise
          promises.forEach((promise) => {
            Promise.resolve(promise).then(resolve).catch(reject);
          });
        });
        return res;
      };

      // 测试用例
      Promise.myRace([
        new Promise((resolve, reject) => {
          setTimeout(() => reject("Promise 2 rejected"), 500);
        }),
        new Promise((resolve) => {
          setTimeout(() => {
            resolve(2);
          }, 1000);
        }), //1s
        new Promise((resolve) => {
          setTimeout(() => {
            resolve(3);
          }, 2000);
        }), //2s
        // Promise.reject(new Error("Failed")), //立即
      ])
        .then((res) => {
          console.log(res);
        })
        .catch((err) => console.error(err));

在这里插入图片描述

(4)promise.any:

promise.any()接收一个包含过个promise实例,返回最先成功执行resolve的promise;如果所有promise都都失败(rejected)时被 reject,返回包含所有失败结果的数组
在这里插入图片描述

      Promise.myAny = function (promises) {
        if (!Array.isArray(promises)) {
          console.error("must be a array");
          return;
        }
        const res = new Promise((resolve, reject) => {
          const errors = [];
          let rejectedCnt = 0;
          // 循环执行每个promise
          promises.forEach((promise, index) => {
            Promise.resolve(promise)
              .then((res) => resolve(res))
              .catch((err) => {
                errors[index] = err;
                rejectedCnt++;
                if (rejectedCnt === promises.length) {
                  reject(
                    new AggregateError(errors, "All promises were rejected")
                  );
                }
              });
          });
        });
        return res;
      };

      // 测试用例
      Promise.myAny([
        // 不能不写resolve
        new Promise((resolve, reject) => {
          setTimeout(() => {
            reject(2);
          }, 1000);
        }), //1s
        new Promise((resolve, reject) => {
          setTimeout(() => {
            reject(3);
          }, 2000);
        }), //2s
        Promise.reject(new Error("Failed")), //立即
        Promise.resolve(1),
      ])
        .then((res) => {
          console.log(res);
        })
        .catch((error) => {
          if (error instanceof AggregateError) {
            console.error("All promises were rejected:");
            error.errors.forEach((err, index) => {
              console.error(`Error ${index + 1}:`, err);
            });
          } else {
            console.error("An unexpected error occurred:", error);
          }
        });

在这里插入图片描述
在这里插入图片描述

2. 实现 对象的深拷贝和浅拷贝

深拷贝:递归赋值所有层,如果是引用类型,那么会新建一个,所以对原值的修改不会影响拷贝值。
浅拷贝:只处理第一层的复制,如果是引用类型,就会复制其引用。所以对于引用属性的值的修改,会相互干扰,因为他们共用一份引用。
(1)浅拷贝:

      function shallowCopy(obj) {
        // typeof null === 'object' 所以单独处理(typeof对于null、array、object都返回object)
        if (typeof obj !== "object" || obj === null) {
          return obj;
        }
        const isArr = Array.isArray(obj);
        const res = isArr ? [] : {};
        if (isArr) {
          for (let i = 0; i < obj.length; i++) {
            res[i] = obj[i];
          }
        } else {
          const keys = Object.keys(obj);
          for (let i = 0; i < keys.length; i++) {
            res[keys[i]] = obj[keys[i]];
          }
        }
        return res;
      }

      console.log(shallowCopy({ a: 1, b: 2 }));

(2)深拷贝:

      function deepCopy(obj) {
        // 如果深拷贝map
        const map = new Map();
        function realCopy(obj) {
          if (typeof obj !== "object" || obj === null) {
            return obj;
          }
          // 如果map中有,则使用map中的数据
          if (map.has(obj)) {
            return map.get(obj);
          }
          const isArr = Array.isArray(obj);
          const res = isArr ? [] : {};
          // 将当前obj和res关联起来
          map.set(obj, res);
          if (isArr) {
            for (let i = 0; i < obj.length; i++) {
              res[i] = realCopy(obj[i]);
            }
          } else {
            const keys = Object.keys(obj);
            for (let i = 0; i < keys.length; i++) {
              const value = obj[keys[i]];
              res[keys[i]] = realCopy(value);
            }
          }
          return res;
        }
        return realCopy(obj);
      }

      const obj = {
        a: {
          a: 1,
          b: 2,
        },
        b: [1, 2, 3],
      };

      const res = deepCopy(obj);
      console.log("res:", res);

      obj.a.a = 3;
      obj.b[0] = 99;
      console.log("obj:", obj);
      console.log("res:", res);

在这里插入图片描述

3. 实现 call, apply, bind

三者均是改变this的指向
call()将实参在对象后依次传递 a,b,c,d
apply()需将实参封装到一个数组中统一传递 [a,b,c,d]
bind() 也是依次传递参数,返回改过this指向的新函数,调用才会执行,即bind(a,b,c,d)() a,b,c,d
在这里插入图片描述
在这里插入图片描述
自己实现call:

      // 手写实现call
      Function.prototype.myCall = function (context, ...args) {
        // 先判断调用对象是不是函数
        if (typeof this !== "function") {
          console.log("type error");
        }
        // 判断context是否传入,null则设为window
        context = context || window;
        // 增加context的临时属性fn,用来存储原来的this指向,也就是函数自己,方便后面调用(为了避免fn与context本身属性重复,使用symbol)
        const fn = Symbol();
        context[fn] = this;
        // 调用方法,此时使用的是context的方法,那么fn 属性所引用的函数(即原始函数)在执行时,this 将指向 context 对象
        const result = context[fn](...args);
        // 删除临时属性,避免 context 对象被 fn 属性污染
        delete context[fn];
        // 返回函数本身调用结果
        return result;
      };

自己实现apply:
在这里插入图片描述
自己实现bind:

      // 手写实现bind
      Function.prototype.myBind = function (context, ...args) {
        // 先判断调用对象是不是函数
        if (typeof this !== "function") {
          console.log("type error");
        }
        // 判断context是否传入,null则设为window
        context = context || window;
        // 闭包存一下当前函数,此时this指向要调用的函数
        const fn = this;
        return function (...innerArgs) {
          // 由于bind语法与call语法类似,此处利用call帮助实现
          return fn.call(context, ...args, ...innerArgs);
          // 也可以使用apply实现
          return fn.apply(context, args.concat(innerArgs));
        };
      };

4. 实现消抖或者节流

(1)消抖的原理是在事件触发后,等待一段时间再执行函数。如果在等待时间内再次触发事件,则重新开始计时。这样可以确保函数在高频率事件触发时,只在最后一次事件触发后才执行一次。常用于 input 事件和 change 事件的处理。

当新函数被多次执行时,每次都会重新计时,只有 delay 长度的时间内,没有触发,才会真正执行原函数。
所以内部设置计时器,该定时器会在 delay 时间之后执行原函数,每次新函数被执行,清除计时器。

手写消抖:

      // 手写防抖:连续时间内多次触发只执行最后一次(只要打断就会重新开始)。利用计时器实现,每次在delay事件之后执行fn,每次新函数被执行,清楚计时器。
      // 优化:用户每次输入都会等一个delay时间才会响应,我们希望用户在第一次输入后立刻响应
      function debounce(fn, delay, immediate) {
        // 入参类型判断
        if (typeof fn !== "function") {
          console.error("First argument must be a function");
        }
        if (typeof delay !== "number" || delay < 0) {
          console.error("Second argument must be a non-negative number");
        }
        // 闭包存储timer,这样,每次result执行时都会是同一个timer
        let timer;
        // 闭包存储一下是否是第一次执行
        let first = true;
        function result(...args) {
          // 清除定时器
          clearTimeout(timer);
          // delay事件后执行fn
          // 箭头函数保证this指向result作用域
          // 每次新函数执行时,重新计时
          timer = setTimeout(() => {
            fn.apply(this, args);
          }, delay);
        }
        // 如果是第一次执行,那么用户期望立即执行一次
        if (first && immediate) {
          fn.apply(this.args);
          first = false;
        }
        return result;
      }

      // 编写测试用例
      function test(a, b) {
        console.log(a, b);
      }
      const debounceTest = debounce(test, 1000);

      // 1s连续触发3次,只执行最后一次
      debounceTest(1, 2);
      debounceTest(2, 3);
      debounceTest(3, 5);

      //3s后执行4,5
      setTimeout(() => {
        debounceTest(4, 5);
      }, 3000);

(2)节流的原理是在规定时间内只执行一次函数,无论这段时间内事件触发了多少次。这可以确保函数在高频率事件触发时,按照固定的时间间隔执行。常用于 scroll 事件的监听
手写节流:

      // 手写节流:连续时间内多次触发只执行一次,强调不要打断我。
      // 记录上次执行时间,计算当前经过时间是否超过最大限制时间,超过则执行并更新上次执行时间
      // 优化:最后一次一定要能执行到
      function throttle(fn, limit, trailing = false) {
        if (typeof fn !== "function") {
          console.error("First argument must be a function.");
        }
        if (typeof limit !== "number" || limit < 0) {
          console.error("Second argument must be a non-negative number.");
        }

        // 记录上一次执行时间
        let lastTime = 0;
        let timer = null;
        function result(...args) {
          const now = Date.now();
          // 如果当前经过时间超过了limit,就执行fn
          if (now - lastTime >= limit) {
            if (timer) {
              // 由于定时器执行时间在limit之后,没有必要每次销毁重建,直接用上一次定时器即可
              clearTimeout(timer);
              timer = null;
            }
            fn.apply(this, args);
            // 更新上次执行时间
            lastTime = now;
          } else if (trailing && !timer) {
            // 如果没有超过limit,设置一个定时器同时清除设次设定的定时器,保障嘴壶一次被执行(这里类似消抖)
            clearTimeout(timer);
            const remainTime = limit - (now - lastTime);
            timer = setTimeout(() => {
              fn.apply(this, args);
              lastTime = Date.now();
              timer = null;
            }, remainTime);
          }
        }
        return result;
      }

      // 编写测试用例
      function test(a, b) {
        console.log(a, b);
      }
      const throttleTest = throttle(test, 1000);

      // 1s连续触发3次,只执行第一次
      throttleTest(1, 2);
      throttleTest(2, 3);
      throttleTest(3, 8);

      //2s后执行4,5
      setTimeout(() => {
        throttleTest(4, 5);
      }, 2000);
      setTimeout(() => {
        throttleTest(6, 7);
      }, 2500);

5. 实现函数的柯里化

函数柯里化(Currying)是一种将一个只能一次性接受多个参数的函数转化为一系列接受一个或多个参数的函数的技术,使得函数可以更灵活地使用和组合。
一个具体函数:
在这里插入图片描述

      // 函数柯里化(Currying)是一种将一个只能一次性接受多个参数的函数转化为一系列接受一个或多个参数的函数的技术,使得函数可以更灵活地使用和组合。
      // 实现函数sum(1)(2)(3)(4)(); //10
      // 实现函数sum(1)(2)(3,4)(); //10
      // 定义外部变量存储参数
      let params = [];
      // 立即执行函数
      const sum = (function () {
        let params = [];
        function result(...args) {
          if (args.length === 0) {
            let temp = 0;
            for (let i = 0; i < params.length; i++) {
              temp += params[i];
            }
            params = [];
            return temp;
          } else {
            params.push(...args);
            return result;
          }
        }
        return result;
      })();

      const a = sum(1)(2)(3)(4)();
      console.log(a);

对于任意函数:

      function curry(fn) {
        if (typeof fn !== "function") {
          console.error("must be a function");
          return;
        }
        let params = [];
        function result(...args) {
          params.push(...args);
          // 如果参数数量超过fn可以接收的数量就执行fn
          // fn.length获取函数参数长度
          if (params.length >= fn.length) {
            const res = fn.apply(this, params);
            params = [];
            return res;
          }
          // 否则返回新函数
          return result;
        }
        return result;
      }

      // 测试用例
      function add(a, b, c, d) {
        return a + b + c + d;
      }
      const curriedAdd = curry(add);
      const suma = curriedAdd(1)(2, 3)(4); //10
      const sumb = curriedAdd(2)(3, 4, 5); //14
      console.log(suma, sumb);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2094712.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

16:螺丝孔和MARK点布局

正版mark布局 ①正面3个。背面3个 ②离板边5mm 螺丝孔 板边

Mako 试玩|编译速度6到飞起!

嗨&#xff01;我是小谷&#xff0c;大家好久不见&#xff5e; 今天想和大家分享的技术是 Mako , 一款编译构建速度比 webpack 快 10 倍&#xff5e;100 倍的前端构建工具。 网上有传言将 Mako 比作前端脚手架里的 鲨鱼心脏 &#xff0c;有了它&#xff0c;前端工程师工作的幸…

重塑未来:碳捕集与存储(CCS)的革命性突破与可持续发展路径

随着全球气候变化的加剧&#xff0c;减少二氧化碳&#xff08;CO₂&#xff09;排放已成为应对气候变化的关键任务之一。碳捕集与存储&#xff08;CCS&#xff09;技术因其能够直接从源头捕捉CO₂并将其安全存储&#xff0c;避免其进入大气层&#xff0c;而受到广泛关注。CCS被…

QT做一个USB HID设备识别软件

1.下载 HidApi库&#xff1a;GitHub - yigityuce/HidApi: Human Interface Device Api (HidApi) with C 2.pro文件添加 DEFINES - UNICODE LIBS -lsetupapi 3.建立三个对象 HidApi hidApi;HidDevice hidDev;//HID设备HidDeviceList devList;//HID设备列表 4.对 HID 设备进…

大连网站建设手机网页页面设计

在现代社会&#xff0c;随着智能手机的普及&#xff0c;越来越多的用户选择通过手机访问网站&#xff0c;这使得移动端网页设计的重要性日益凸显。大连作为一个经济和文化中心&#xff0c;网站建设行业也在不断发展。针对大连的网站建设&#xff0c;手机网页页面设计需要特别注…

在街子古镇游的台湾自媒体人

刚刚看到《网易首页 > 网易号》于昨&#xff08;8月31日逾23时&#xff09;天深夜发布《崇州看点》的新闻报道《台湾自媒体人天府游记&#xff0c;被崇州的这个地方深深吸引……》&#xff0c;虽觉得新鲜但并不感到惊奇。因为在去年12月5日&#xff0c;《人民日报海外版》和…

CAS单点登录说明文档

CAS单点登录说明文档 目录 1. 下载CAS 2. 下载xmlsectool 3. 安装xmlsectool 4. 打包CAS 5. 连接服务器 6. 安装Tomcat服务器 7. 创建CAS程序 8. 修改CAS界面 9. 修改CAS服务端口 10. 修改CAS服务名称 11. 修改CAS日志路径 12. 创建数据库 13. 启动CA…

PD快充协议

表格中电压电流档位的电流都是在该电压下输出的最大电流。在充电的过程当中&#xff0c;充电器输出的电流会根据充电设备的需求进行动态调整&#xff0c;不是说握手了20V 5A档位充电器输出的电流就一直都是5A。 这个屏幕显示了几种常见的快充协议及其支持的电压、电流和功率。这…

kubeadm部署 Kubernetes(k8s) 高可用集群【V1.28 】

kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具。 calico.yaml kubernertes-dashboard.yaml 1. 安装要求 在开始之前&#xff0c;部署Kubernetes集群机器需要满足以下几个条件&#xff1a; 10台机器&#xff0c;操作系统Openeuler22.03 LTS SP4硬件配置&…

2024年【制冷与空调设备运行操作】找解析及制冷与空调设备运行操作实操考试视频

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 制冷与空调设备运行操作找解析参考答案及制冷与空调设备运行操作考试试题解析是安全生产模拟考试一点通题库老师及制冷与空调设备运行操作操作证已考过的学员汇总&#xff0c;相对有效帮助制冷与空调设备运行操作实操…

【大模型】Agent基础知识

目录 1. 基本框架2. 常见推理模式2.1 ReAct: Synergizing Reasoning and Acting in Language Models2.2 Reflection2.3 LATS: Language Agents Tree Search 3. 微调3.1 全模型微调&#xff08;Full Model Fine-Tuning&#xff09;3.2 冻结部分层微调&#xff08;Layer-wise Fin…

IPC核间通信底层原理:以PL320为例

什么是IPC核间通信 讲到IPC可能很多同学想到的是InterProcess Communication进程间通信&#xff0c;但是本文主要是讲另一种Inter-processor communication,处理器间通信&#xff0c;也叫核间通信&#xff0c;名字很像不要搞混。 为什么需要核间通信 现在的芯片系统非常复杂…

企业微信dll,最新版dll

1.基础信息获取,如登录用户信息、联系人列表、群组列表等。 2.联系人操作,如修改备注、添加删除联系人等。 3.群操作,如创建群聊、邀请添加成员、修改群信息等。 4.消息发送,支持文本、图片、文件、位置等不同类型消息的发送。 5.支持通过DLL调用实现自动化功能,如机器人自动回…

Python一些可能用的到的函数系列131 发送钉钉机器人消息

说明 来自顾同学的助攻 钉钉机器人可以用来发送一些重要的系统消息&#xff0c;例如磁盘将满等等。原本还可以有更强的功能&#xff0c;就是监听群里的消息&#xff0c;然后做出反应&#xff0c;不过这个好像要买企业版&#xff0c;贵的毫无意义。 钉钉发消息有几种模式&#…

python中pip源配置

文章目录 1、前言2、pip下载源配置 1、前言 conda环境的源配置&#xff0c;相关博客有很多&#xff0c;这里不再赘述。在我使用梯子后&#xff0c;使用pip进行库安装&#xff0c;总是出现错误情况&#xff0c;如下所示&#xff1a; 2、pip下载源配置 因此&#xff0c;在这里进…

Django+Vue农产品销售系统的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 需要的环境3.2 Django接口层3.3 实体类3.4 config.ini3.5 启动类3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质创作者&…

「Claude3.5」全面超越「gpt-4o」,我用它做了个贪吃蛇,玩了一整天!

大家好&#xff0c;我是凡人。 就在昨天晚上Anthropic在X上连续发了4条动态来高调宣布他们的Claude 3.5 Sonnet中杯的版本已经全面向公众开放使用&#xff0c;大批的技术博主连夜测试&#xff0c;纷纷给出的不低的评价。 而这还仅仅是开胃小菜&#xff0c;官方宣称今年晚些时候…

37集【重要】编译ESP-RTC工程并运行在01-RTC开发板中

37集【重要】编译ESP-RTC工程并运行在01-RTC开发板中 开发板样子这样的&#xff1a; 还有配套的外壳&#xff1a; 开发板介绍文档如下&#xff0c;有需要的可以联系三哥&#xff0c;三哥介绍这位大拿给你&#xff1a; https://www.kdocs.cn/l/cqAyZ0T3Q06Y 我们把d:\Espre…

单片机编程魔法师-消息处理

消息机制 消息处理的编程思路是当某件事产生后只发送一条事件产生消息以通知相应执行机构执行的一种编程思路。 消息定义 什么是消息&#xff0c;消息是一个指示&#xff0c;可以是数字&#xff0c;字符串&#xff0c;字符或者是任何形式的其他标识符 消息定义的形式与消息…

模型 冯/诺依曼思维模型

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。分解问题&#xff0c;创新整合&#xff0c;高效解决。 1 冯/诺依曼思维模型的应用 1.1 景区创建5A级旅游景区提升规划 在面对如何将某个景区创建为5A级旅游景区的复杂任务时&#xff0c;可以采用冯诺…