前端面试必会的手撕代码题,四天带你学完(第二天)

92 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情

  • 手写 Promise.all
  • 手写 Promise.race
  • 手写防抖函数
  • 手写节流函数
  • 手写类型判断函数

1. 手写 Promise.all

1) 核心思路

  1. 接收一个 Promise 实例的数组或具有 Iterator 接口的对象作为参数
  2. 这个方法返回一个新的 promise 对象,
  3. 遍历传入的参数,用Promise.resolve()将参数"包一层",使其变成一个promise对象
  4. 参数所有回调成功才是成功,返回值数组与参数顺序一致
  5. 参数数组其中一个失败,则触发失败状态,第一个触发失败的 Promise 错误信息作为 Promise.all 的错误信息。

2)实现代码

一般来说,Promise.all 用来处理多个并发请求,也是为了页面数据构造的方便,将一个页面所用到的在不同接口的数据一起请求过来,不过,如果其中一个接口失败了,多个请求也就失败了,页面可能啥也出不来,这就看当前页面的耦合程度了

 function promiseAll(promises) {
   return new Promise(function(resolve, reject) {
     if(!Array.isArray(promises)){
         throw new TypeError(`argument must be a array`)
     }
     var resolvedCounter = 0;
     var promiseNum = promises.length;
     var resolvedResult = [];
     for (let i = 0; i < promiseNum; i++) {
       Promise.resolve(promises[i]).then(value=>{
         resolvedCounter++;
         resolvedResult[i] = value;
         if (resolvedCounter == promiseNum) {
             return resolve(resolvedResult)
           }
       },error=>{
         return reject(error)
       })
     }
   })
 }
 // test
 let p1 = new Promise(function (resolve, reject) {
     setTimeout(function () {
         resolve(1)
     }, 1000)
 })
 let p2 = new Promise(function (resolve, reject) {
     setTimeout(function () {
         resolve(2)
     }, 2000)
 })
 let p3 = new Promise(function (resolve, reject) {
     setTimeout(function () {
         resolve(3)
     }, 3000)
 })
 promiseAll([p3, p1, p2]).then(res => {
     console.log(res) // [3, 1, 2]
 })

2. 手写 Promise.race

该方法的参数是 Promise 实例数组, 然后其 then 注册的回调方法是数组中的某一个 Promise 的状态变为 fulfilled 的时候就执行. 因为 Promise 的状态只能改变一次, 那么我们只需要把 Promise.race 中产生的 Promise 对象的 resolve 方法, 注入到数组中的每一个 Promise 实例中的回调函数中即可.

 Promise.race = function (args) {
   return new Promise((resolve, reject) => {
     for (let i = 0, len = args.length; i < len; i++) {
       args[i].then(resolve, reject)
     }
   })
 }

3. 手写防抖函数

函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。

 // 函数防抖的实现
 function debounce(fn, wait) {
   let timer = null;
 ​
   return function() {
     let context = this,
         args = arguments;
 ​
     // 如果此时存在定时器的话,则取消之前的定时器重新记时
     if (timer) {
       clearTimeout(timer);
       timer = null;
     }
 ​
     // 设置定时器,使事件间隔指定事件后执行
     timer = setTimeout(() => {
       fn.apply(context, args);
     }, wait);
   };
 }

4. 手写节流函数

函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。

 // 函数节流的实现;
 function throttle(fn, delay) {
   let curTime = Date.now();
 ​
   return function() {
     let context = this,
         args = arguments,
         nowTime = Date.now();
 ​
     // 如果两次时间间隔超过了指定时间,则执行函数。
     if (nowTime - curTime >= delay) {
       curTime = Date.now();
       return fn.apply(context, args);
     }
   };
 }

5. 手写类型判断函数

 function getType(value) {
   // 判断数据是 null 的情况
   if (value === null) {
     return value + "";
   }
   // 判断数据是引用类型的情况
   if (typeof value === "object") {
     let valueClass = Object.prototype.toString.call(value),
       type = valueClass.split(" ")[1].split("");
     type.pop();
     return type.join("").toLowerCase();
   } else {
     // 判断数据是基本数据类型的情况和函数的情况
     return typeof value;
   }
 }