粗解「二三梯隊互聯網公司」前端面試題

前言:暫時先粗解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;

複製代碼
相關文章
相關標籤/搜索