粗解“二三梯队互联网公司”前端面试题

前言:暂时先粗解10题。题目会不按期更新(解题若有误,可在评论区贴出~你们共同窗习进步)node

1. 实现Promise “乞丐版...”

/**
 * 【模拟Promise】
 */
class myPromise {
  constructor(callback) {
    this.status = 'pending' 
    this.resMsg = ''
    this.errMsg = ''
    this.successCallBack = null
    this.errCallBack = null
    callback((res) => {
      this.resMsg = res
      this.successCallBack && this.successCallBack(this.resMsg) //  pending ==> resolved 状态时, 如有暂存回调函数, 读取暂存回调函数
      this.status = 'resolved'
    }, (err) => {
      this.errMsg = err
      this.errCallBack && this.errCallBack(this.errMsg)
      this.status = 'rejected'
    })
    this.callback = callback
  }
  then(successCallBack) {
    console.log('---- then ---', this.status)
    if(this.status === 'resolved') {
      successCallBack(this.resMsg)
    }
    if(this.status === 'pending') {
      console.log(successCallBack)
      this.successCallBack = successCallBack // 若是还在pending状态,先暂存回调函数
    }
    return this;
  }
  catch(errCallBack) {
    console.log('---- catch ---', this.status)
    if(this.status === 'rejected') {
      errCallBack(this.errMsg)
    }
    if(this.status === 'pending') {
      console.log(errCallBack)
      this.errCallBack = errCallBack
    }
    return this;
  }
}

/**
 *  测试用例一
 */
new myPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('测试success')
  }, 2000)
}).then(res => {
  console.log('res', res)
}).catch(err => {
  console.log('err', err)
})

/**
 *  测试用例二
 */
new myPromise((resolve, reject) => {
  setTimeout(() => {
    reject('测试error')
  }, 5000)
}).then(res => {
  console.log('res', res)
}).catch(err => {
  console.log('err', err)
})


复制代码

2. 用promise实现Promise.all

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功了')
  }, 3000);
  
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 4000);
})

let p3 = Promise.reject('失败')

/**
 * 【模拟Promise.all】
 */
class myPromise{
  constructor() {

  }
  static all(promises) {
    // 确保promises为array
    if(!Array.isArray(promises)) {
      throw new Error('大哥,麻烦输入promise数组啊')
    }
    let results = []
    let isRejected = false
    return new Promise((resolve, reject) => {
      promises.map((item, index) => {
        item.then(res => {
          if(!isRejected) {
            results.push(res)
            if(index === promises.length - 1) {
              promises.length === results.length && resolve(results)
            }
          }
          
        }).catch(err => {
          reject(err)
          isRejected = true
        })
      })
      
    })
    
  }
}

myPromise.all([p1,p2]).then(res => {
  console.log(res, 'res1')
}).catch(err => {
  console.log(err, 'err1')
})

myPromise.all([p1,p2,p3]).then(res => {
  console.log(res, 'res2')
}).catch(err => {
  console.log(err, 'err2')
})

复制代码

3. 用promise实现Promise.race

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功了')
  }, 3000);
  
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('失败了')
  }, 1000);
})

class myPromise{
  constructor() {

  }
  static race(promises) {
    // 确保promises为array
    if(!Array.isArray(promises)) {
      throw new Error('大哥,麻烦输入promise数组啊')
    }
    let hasResult = false
    return new Promise((resolve, reject) => {
      promises.map((item, index) => {
        item.then(res => {
          hasResult || resolve(res)
          hasResult = true
        }).catch(err => {
          hasResult || reject(err)
          hasResult = true
        })
      })
      
    })
    
  }
}

myPromise.race([p1,p2]).then(res => {
  console.log(res, 'res1')
}).catch(err => {
  console.log(err, 'err1')
})

复制代码

4. 控制promise队列的并发上限

// Tips: 并发20个promise,并发上限是10个,队列先入队前10个promise,其中有promise执行成功,队列外剩余的promise依次入队(保证队列中始终保持10个上限)web

// 生成promise
const fetch = (duration , index) => {
  return function() {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({duration, index})
      }, duration);
    })
  }
  
}


/**
 * 控制promise并发数 (暂时默认所有 success)
 * Tips: Promise实例化后,即开始执行。因此promise数超上限时,暂时解决方案是:先初始化limit个promise,以后
 * 
 * @param {Number} total 总promise条数
 * @param {Number} max 最大并发数
 * @param {Funtion} callback 全部promise执行后的回调
 * @return 
 */
const concurrencyFetch = (total, max, callback) => {
  let currentFetchs = [];
  
  let maxlen = Math.min(total, max)
  let i = 0;
  fetchs = initPromises(total)

  // 请求数 未达到并发上限, 直接走promise.all便可
  if(fetchs.length < max) {
    Promise.all(fetchs.reduce((total, item) => {
      return [...total, item.pro()]
    }, [])).then(() => {
      callback('所有执行完了')
    })
    return false;
  }
  currentFetchs = fetchs.slice(0, max)
  // 达到最大并发数
  function concurrencyLimit() {
    
    /* currentFetchs.splice() */
    /* let currentFetchs = fetchs.slice(i, i + max) */
    /* console.log('---------达到并发瓶颈了---------', i, fetchs) */
    console.log('当前---', i, currentFetchs)
    debugger
    Promise.race(currentFetchs.reduce((total, item) => {
      return [...total, item.pro()]
    }, [])).then((res) => {
        if(max + i < total) {
          const removeIndex = currentFetchs.findIndex(item => item.index === res.index)
          let fastPromise = currentFetchs.splice(removeIndex, 1)
          console.log('移除队列中最快执行的promise:', fastPromise)
          i++;
          currentFetchs = [...currentFetchs, fetchs[max + i - 1]]
          concurrencyLimit()
        } else {
          // 已经所有塞入 currentFetchs队列
          Promise.all(currentFetchs.reduce((total, item) => {
            return [...total, item.pro()]
          }, [])).then(() => {
            callback('所有执行完了')
          })
        }  
    })
  }
  concurrencyLimit()
}

// 生成promise list
const initPromises = (len, startIndex = 0) => {
  let arr = [];
  new Array(len).fill('').map((_ , i) => {
    let randomNum = Math.floor(Math.random()*10 + 1) * 1000
    arr.push({pro: fetch(randomNum, startIndex + i), index: startIndex + i, randomNum})
  })
  console.log('生成的promise列表: ', arr)
  return arr
} 

concurrencyFetch(15 , 10, (res) => {
  console.log(res, '都ok了~')
})

复制代码

5. 请手写一个sleep函数(多种方案。。。)

// Tips: 暂时只想到两种,有其余方案的能够评论区留言哈~数组

实现方案一:(async await | promise) 我的感受是最容易想到的方案promise

/**
 * 线程阻塞
 * 
 * @param {Number}
 * @return 
 */
const sleep = async (delay) => {
  const pro = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`----- 线程阻塞 ${delay/1000} s了 -----`)
    }, delay);
  })
  const res = await pro
  console.log(res)
}

复制代码

实现方案二:(时间戳) 浏览器

const sleep = async (delay) => {
  const startTime = new Date().getTime();
  console.log(startTime)
  while(new Date().getTime() - startTime < delay) {
    continue
  }
  console.log(`----- 线程阻塞 ${delay/1000} s了 -----`)
}

复制代码

6. 实现一个LazyMan,能够按照如下方式调用:

LazyMan(“Hank”)输出: Hi! This is Hank!bash

LazyMan(“Hank”).sleep(10).eat(“dinner”) 输出 Hi! This is Hank! //等待10秒.. Wake up after 10 Eat dinner~闭包

LazyMan(“Hank”).eat(“dinner”).eat(“supper”) 输出 Hi This is Hank! Eat dinner~ Eat supper~并发

以此类推。dom

class LazyMan {
  constructor(name) {
    this.name = name
    this.asyncFun = Promise.resolve(this)
    console.log(`--------- Hi This is ${this.name}! ---------`)
  }
  sleep(delay) {
    console.log('sleep')
    this.asyncFun = this.asyncFun.then(() => {
      return new Promise(resolve => {
        setTimeout(() => {
          console.log(`--------- Wake up after ${delay/1000}s ----------`)
          resolve()
        }, delay);
      })
    })
    return this; //提供 ”链式调用“
  }
  eat(food) {
    console.log('eat')
    this.asyncFun = this.asyncFun.then(res => {
      console.log(`--------- Eat ${food}~ ---------`)
      return Promise.resolve()
    })
    return this;
  }
}

new LazyMan('小菜比').sleep(4000).eat('豆浆').eat('油条').sleep(2000).eat('炒年糕')

复制代码

7. 节流

举个例子:冲啊,兄弟们,拿lol首胜赚金币了。。。腾讯baba为了防止你被拉去杨教授那电疗,首胜的周期是一天一次。若是不设置周期,每盘都能触发首胜。。。你怕是要累趴在电脑前。。。 这就是咱们传说中的 节流 async

/**
 * 节流
 * 
 * @param {Function} fn 须要节流的函数
 * @param {Number} duration 触发fn间隔时间, 单位ms
 * @param {Boolean} immediately 是否当即执行
 * @return 
 */
function throttle (fn, duration, immediately = true) {
  let timer = null
  
  // 为啥用闭包??? 把局部变量驻留在内存,避免使用全局变量
  return function() {
    if(!timer) { 
      if(immediately) {
        fn()
        timer = setTimeout(() => {
          timer = null
        }, duration);  
      } else {
        timer = setTimeout(() => {
          fn()
          timer = null
        }, duration); 
      }
 
    }
  }
  
}

function show() {
  console.log('优雅的切图仔')
}

setInterval(throttle(show, 5000, true), 1000);

复制代码

8. 防抖

举个例子: 我要减肥(固然博主并不须要减肥,反而须要增肥,哈哈)???立个flag...劳资15天不吃肉!谁吃谁煞b。。一天两天我咬牙坚决信念,终于13天过去了,第十四天(我。。。实在忍忍忍不住了,偷偷吃了包猪肉铺,nice成功“破戒了”,15天flag从新刷新,前功尽弃咯。。。) 这就是咱们传说中的 防抖

/**
 * 防抖
 * 
 * @param {Function} fn 须要防抖的函数
 * @param {Number} duration 禁止触发fn的间隔, 单位ms
 * @param {Boolean} immediately 是否当即执行
 * @return 
 */
function debounce(fn, duration, immediately = true) {
    let timer = null
    
    // 为啥用闭包??? 把局部变量驻留在内存,避免使用全局变量
    return function() {
        if(immediately) {
          timer ? clearTimeout(timer) : fn()
          timer = setTimeout(() => {
            timer = null
          }, duration);  
        } else {
          timer && clearTimeout(timer)
          timer = setTimeout(() => {
            timer = null
            fn()
          }, duration); 
          console.log(timer)
        }
    }
    
  }

复制代码

9. js中,0.1+0.2为何等于0.30000000000000004,如何经过代码解决这个问题

function add(...args) {
  let radixPointArr = args.reduce((total, item) => {
    item = '' + item
    let itemRadixPoint = item.indexOf('.')
    if(itemRadixPoint >= 1) {
      return [...total, item.length - itemRadixPoint - 1]
    } else {
      return [...total, 0]
    }
  }, [])
  const radixLen = Math.max(...radixPointArr)
  const result = args.reduce((total,item) => {
    return total + item * Math.pow(10,radixLen)
  }, 0)
  return result / Math.pow(10,radixLen)
}
add(0.1,0.222,0.3, 3232, 0.66666)

复制代码

10. 如何实现localStorage的时效性

/**
 * 【localStorage设置时效性】
 * 
 *  Tips: 这个就不要在node里跑了。web storage 针对浏览器~
 * @param {String} key 
 * @param {Any} value
 * @param {Number} expired 保质期 单位“秒”(这时候,一批吃货投来“不屑”的目光)
 */
class myStorage {
  constructor() { 

  }
  // 默认无限大,暂时就99999999s吧~
  static set(key, value, expired = 99999999) {
    localStorage.setItem(key, JSON.stringify({
      value,
      expiredTimestamp: new Date().getTime() + expired * 1000
    }));
  }
  static get(key) {
    if(localStorage.getItem(key)) {
      let result = JSON.parse(localStorage.getItem(key))
      console.log(result)
      const nowTimestamp = new Date().getTime()
      if(nowTimestamp > result.expiredTimestamp) {
        console.log('都过时了,看你妹啊~')
        this.remove(key)
      } else {
        console.log(result.value)
      }
    } else {
      console.log('storage已经被删除了,赶忙去建立一个吧~')
    }

  }
  static remove(key) {
    localStorage.getItem(key) && localStorage.removeItem(key)
  }
}

myStorage.set('name', '我是小菜比', 10)
myStorage.get('name')

复制代码

11. JS函数柯里化

实现add(1)(2)(3) = 6; add(1, 2, 3)(4) = 10; add(1)(2)(3)(4)(5) = 15;

复制代码
相关文章
相关标签/搜索