前端面試筆記

const arr = [1,2,[2,3],[5,6],7,0]
const fn = (arr, newArr) => {
    arr.forEach(item => {
      if(Array.isArray(item)) {
        fn(item, newArr)
      } else {
        newArr.push(item)
    }
  })
  return newArr
}
console.log(fn(arr, [])) //  [1, 2, 2, 3, 5, 6, 7, 0]

方法2:flat()
flat()接受一個(gè)數(shù)值參數(shù),表示需要拉平的層級(jí),Infinity表示無限層級(jí)的拉平。

[1, 2, [3, [4, 5]]].flat()  // [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5]
arr.flat(Infinity)          // [1, 2, 2, 3, 5, 6, 7, 0]

方法3:reduce() 函數(shù):遍歷數(shù)組,每次返回上一次的處理后的值。reduce(function(prev,next){}, initVal), initVal 為prev的初始值,不傳initVal,prev的初始值為數(shù)組的第一個(gè)元素,next為第二個(gè)數(shù)組元素。

// reduce 每次的prev都是[],所以采用數(shù)組的concat()
const fn2 = arr =>  {
    return arr.reduce((prev,next) => { 
        return prev.concat(Array.isArray(next) ? fn2(next) : next)
    },[])
}
  • 防抖debounce
    觸發(fā)高頻事件后n秒內(nèi)會(huì)執(zhí)行一次,如果n秒內(nèi)再次觸發(fā),則重新計(jì)算時(shí)間進(jìn)行執(zhí)行。
    即:觸發(fā)高頻事件每隔一定時(shí)間執(zhí)行一次。
    場景:防抖常應(yīng)用于用戶進(jìn)行搜索輸入節(jié)約請求資源,滾動(dòng)事件等
const debounce = (fn,time = 500) => {
   let timer = 0
   return function() {
      if(timer) clearTimeout(timer)
      timer = setTimeout(() => {
        fn.apply(this)
      }, time)
    }
  }
// 高頻觸發(fā)函數(shù)mousemove  
const box = document.getElementsByClassName('box')[0]
const fn4 = () => console.log('觸發(fā)函數(shù)')
box.addEventListener('mousemove', debounce(fn4))
  • 節(jié)流throttle
    n秒內(nèi)觸發(fā)高頻事件只會(huì)執(zhí)行一次, 所以節(jié)流會(huì)稀釋函數(shù)的執(zhí)行頻率。
    場景:節(jié)流常應(yīng)用于防重復(fù)點(diǎn)擊。
const throttle = (fn, time = 100) => {
    let isFinished = false
    return function() {
      if(isFinished) {
            clearTimeout(timer)
            return 
      }
      timer = setTimeout(() => {
        fn.apply(this)
        isFinished = true
      }, time)
  }
}
// 防重復(fù)點(diǎn)擊
const box = document.getElementsByClassName('box')[0]
const fn4 = () => console.log('觸發(fā)函數(shù)')
box.addEventListener('click', throttle(fn4))
  • arguments.callee
    在函數(shù)內(nèi)部,有兩個(gè)特殊的對象:arguments 和 this。其中, arguments 的主要用途是保存函數(shù)參數(shù), 但這個(gè)對象還有一個(gè)名叫 callee 的屬性,該屬性是一個(gè)指針,指向擁有這個(gè) arguments 對象的函數(shù)。
    場景:使用遞歸的過程中會(huì)根據(jù)條件來調(diào)用函數(shù)本身,這樣會(huì)導(dǎo)致一個(gè)問題,函數(shù)內(nèi)部存在一個(gè)我們自定義的函數(shù)名,不利于再次封裝。
// 使用函數(shù)名
const fn = function(num) {
    if(num > 1) {
      return num * fn(num - 1)
    } else {
        return 1
    }
 }
// 使用arguments.callee
const fn = function(num) {
  if(num > 1) {
      return num * arguments.callee(num - 1)
  } else {
    return 1
  }
}
  • 函數(shù)珂里化
    指的是將一個(gè)接受多個(gè)參數(shù)的函數(shù)變?yōu)榻邮芤粋€(gè)參數(shù),返回一個(gè)函數(shù)的固定形式,便于再次調(diào)用。
// 求和:curry(1)(2)(3)(4) 及curry(2)(2,3,4)(5)
const curry = function() {
  const _arg = [...arguments]
  const fn = function() {
      _arg.push(...arguments)
      return fn
  }
  fn.toString = () => _arg.reduce((prev, next) => prev + next)
  return fn
}
  • 原型(prototype)、原型鏈和原型繼承
    image.png
  1. 所有引用類型(函數(shù),數(shù)組,對象)都擁有proto屬性(隱式原型)
  2. 所有函數(shù)擁有prototype屬性(顯式原型)(僅限函數(shù))
  3. 原型鏈的實(shí)現(xiàn):查找屬性,如果本身沒有,則會(huì)去__ proto __ 中查找,也就是構(gòu)造函數(shù)的顯式原型中查找,如果構(gòu)造函數(shù)中也沒有該屬性,因?yàn)闃?gòu)造函數(shù)也是對象,也有 __ proto __,那么會(huì)去它的顯式原型中查找,一直到null,如果沒有則返回undefined。
  4. 構(gòu)造函數(shù)都有一個(gè)prototype屬性指向原型對象,原型對象通過constructor屬性指向構(gòu)造函數(shù)本身。
    實(shí)例對象都有一個(gè)__ proto __屬性指向原型對象
  5. 實(shí)例對象的__ proto __指向構(gòu)造函數(shù)的prototype,從而實(shí)現(xiàn)繼承。
    prototype對象相當(dāng)于特定類型所有實(shí)例對象都可以訪問的公共容器
  6. 繼承:
    (1)原型鏈繼承
    (2)call和apply。區(qū)別call(this,p1,p2,p3,...),apply(this.[...params])第二個(gè)開始為參數(shù)集合
    (3)方法繼承:深度拷貝原型,然后更改constructor屬性
  person._ proto _ = Object.create(People.prototype)
  peson.constructor = Person
  或者
  Person.prototype = Object.create(People.prototype)
  peson.constructor = Person

https://blog.csdn.net/yucihent/article/details/79424506
https://zhuanlan.zhihu.com/p/35790971

  • new 操作符的實(shí)現(xiàn)
    思路:新創(chuàng)建一個(gè)空對象,新對象原型賦值構(gòu)造函數(shù)的原型,執(zhí)行構(gòu)造函數(shù)并綁定this到新對象。(新對象擁有構(gòu)造函數(shù)及原型上的屬性和方法)
function _MyNew(obj) {
    // 創(chuàng)建一個(gè)空對象
    let newObj = Object.create()
    // 設(shè)置空對象的原型
    newObj.__proto__ = obj.prototype
    // 使用apply綁定this,執(zhí)行構(gòu)造函數(shù)
    const res = obj.apply(newObj)
    const isObject = typeof res === 'object' && res !== null;
    const isFunction = typeof res === 'function';
    return isObject || isFunction ? res : obj;
}
  • 宏任務(wù)與微任務(wù)
    Javascript 是單線程腳本語言。分為同步任務(wù)(立即執(zhí)行)、異步任務(wù)(不會(huì)立即執(zhí)行),異步任務(wù)分為宏任務(wù)、微任務(wù),異步任務(wù)進(jìn)過事件注冊模塊將回調(diào)添加到任務(wù)隊(duì)列進(jìn)行處理。
    (1)宏任務(wù)
    常見的有:
    setTimeout
    setInterval
    requireAnimationFrame請求動(dòng)畫幀(瀏覽器獨(dú)有)
    UI render(瀏覽器獨(dú)有)
    setImmediate(node獨(dú)有)
    I/O
    (2)微任務(wù)
    常見的有:
    Promise.then()
    Process.nextTick(node獨(dú)有)
    Object.observe
    MutationObserve
    (1)當(dāng)一個(gè)異步任務(wù)進(jìn)入棧的時(shí)候,主線程會(huì)判斷是同步還是異步任務(wù),如果是同步任務(wù),則立即執(zhí)行;如果是異步任務(wù),則將該任務(wù)交給異步處理模塊處理,當(dāng)異步處理完到觸發(fā)條件的時(shí)候,根據(jù)任務(wù)的類型,將回調(diào)壓入隊(duì)列之中。如果是宏任務(wù),則新增一個(gè)宏任務(wù),任務(wù)隊(duì)列中的宏任務(wù)可以有多個(gè);如果是微任務(wù),則直接壓入微任務(wù)隊(duì)列。
    (2)當(dāng)兩個(gè)宏任務(wù)隊(duì)列的時(shí)候,第一個(gè)宏任務(wù)隊(duì)列中有一個(gè)微任務(wù):即當(dāng)在執(zhí)行宏任務(wù)的時(shí)候,遇到微任務(wù)則將微任務(wù)加入微任務(wù)隊(duì)列,最后剩下一個(gè)微任務(wù)和一個(gè)宏任務(wù),此時(shí)會(huì)先執(zhí)行微任務(wù),再執(zhí)行宏任務(wù)
    (3)宏任務(wù)與微任務(wù)在瀏覽器和node中的執(zhí)行有所差異
    瀏覽器:執(zhí)行宏任務(wù),然后執(zhí)行宏任務(wù)對應(yīng)的微任務(wù),重復(fù)進(jìn)行。由于執(zhí)行棧的入口為script,而全局任務(wù)為宏任務(wù),所以當(dāng)棧為空的時(shí)候,同步任務(wù)執(zhí)行完畢,會(huì)先執(zhí)行微任務(wù)隊(duì)列。微任務(wù)執(zhí)行完畢,會(huì)讀取宏任務(wù)隊(duì)列中最前的任務(wù),執(zhí)行宏任務(wù)的過程中,如果遇到微任務(wù),則依次加入微任務(wù)隊(duì)列,??蘸?,再次讀取微任務(wù),依次類推。
    node:執(zhí)行完全部的宏任務(wù),然后執(zhí)行微任務(wù)。
      console.log('1')
      setTimeout(()=>{
          console.log('2');
          new Promise(resolve=>{
            console.log('3')
            resolve();
          }).then(()=>{
            console.log('4')
          })
      },0)
      new Promise(resolve=>{
            console.log('5')
            resolve();
          }).then(()=>{
            console.log('6')
          }) 
      setTimeout(()=>{
          console.log('7');
      },0)   
      setTimeout(()=>{
          console.log('8');
          new Promise(resolve=>{
            console.log('9')
            resolve();
          }).then(()=>{
            console.log('10')
          })
      },0)
      new Promise(resolve=>{
            console.log('11')
            resolve();
          }).then(()=>{
            console.log('12')
          })
      console.log('13');

答案://1, 5,11,13,6,12,2,3,4,7,8,9,10
轉(zhuǎn) https://blog.csdn.net/qq_42833001/article/details/87252890

  • 渲染幾萬條數(shù)據(jù)不卡住頁面借助請求動(dòng)畫幀requestAnimationFrame
/**
        功能: 勻速滾動(dòng)到頁面頂部
        @dom: 事件源DOM,
        @speed: 總份數(shù)
      */
      function scrollTopHandler(dom, speed = 500) {
        // 滾動(dòng)的高度
        let doc_scrollTop
        let i = 0
        // requestAnimationFrame 的執(zhí)行函數(shù)
        const gotoTop = function() {
          // 將滾動(dòng)高度分為1000份, 每份為unit的高度
          const unit = doc_scrollTop / speed
          // 每次執(zhí)行10份unit的距離
          i += 10
          // 計(jì)算每次滾動(dòng)的高度
          const scrollTop = doc_scrollTop - i * unit > 0 ? doc_scrollTop - i * unit : 0
          document.documentElement.scrollTop = scrollTop
          // 判斷執(zhí)行分?jǐn)?shù)i小于speed 總分?jǐn)?shù),則繼續(xù)執(zhí)行
          if (i <= speed) {
            window.requestAnimationFrame(gotoTop)
          } else {
            // 重置
            i = 0
          }
        }
        // 點(diǎn)擊事件監(jiān)聽
        const handler = function() {
          doc_scrollTop = document.documentElement.scrollTop
          window.requestAnimationFrame(gotoTop)
        }
        dom.addEventListener('click',handler,false)
      }
      // 調(diào)用方法
      const dom = document.querySelector('#gotoTop')
      scrollTopHandler(dom)
    function bubbleSort(arr) {
        for(let i = 0; i < arr.length - 1; i++){ 
          for(let j = i + 1; j <= arr.length - 1; j++) { 
            if(arr[i] > arr[j]) {
              const tempVal = arr[i]
              arr[i] = arr[j]
              arr[j] = tempVal
            }
          }
        }
        return arr
      }

雙指針-快速排序
思想:來自于快速排序,在一個(gè)有序的數(shù)組(從小到大)中最左邊一定是最小值,最右邊是最大值。我們可將最小值與最大值相加與目標(biāo)值進(jìn)行比較,如果兩數(shù)之和大于目標(biāo)值,我們就讓最大值小一點(diǎn)(讀取第二個(gè)最大值),如果兩數(shù)之和小于目標(biāo)值,我們就讓最小值大一點(diǎn)(讀取第二個(gè)最小值),如果兩數(shù)之和剛好等于目標(biāo)值,保存最大值,最小值,并且讓最大值小一點(diǎn),最小值大一點(diǎn)。需要注意的是前提條件是數(shù)組必須有序!

  function sum2(arr,tartgetVal) {
        const resulutArr = []
        const sortArr = arr.sort(function(a,b){
          return a - b
        })
        let start = 0 
        let end = sortArr.length - 1
        while(start < end) {
          if (sortArr[start] + sortArr[end] === tartgetVal) {
            resulutArr.push([sortArr[start], sortArr[end]])
            start += 1
            end -= 1
          } else if (sortArr[start] + sortArr[end] >= tartgetVal) {
            end -= 1
          } else {
            start += 1
          }
        }
        console.log(resulutArr)
        return resulutArr
      }
  const arr = [1, 4, 3, 2, 6, 5]
  sum2(arr,6)
function sum3(arr, targetVal) {
        const sortArr  = arr.sort((a,b) => {
          return a - b; 
        })
        console.log(sortArr)
        let result = []
        const len = sortArr.length - 3
        for(let i = 0; i < sortArr.length -3; i++) {
          let left = i + 1, right = sortArr.length - 1
          while(left < right) {
            if(sortArr[i] + sortArr[left] + sortArr[right] === targetVal) {
              result.push([sortArr[i], sortArr[left], sortArr[right]])
              left += 1
              right -= 1
            } else if (sortArr[i] + sortArr[left] + sortArr[right] > targetVal) {
              right -= 1
            } else {
              left += 1
            }
          }
        }
        console.log(result)
        return result
      }
    const arr = [1, 4, 3, 2, 6, 5, 9, 8, 21, 22, 34, 10, 14]
    sum3(arr, 10)
// 
 [Array(3), Array(3), Array(3)]
  0:  [1, 3, 6]
  1: [1, 4, 5]
  2: [2, 3, 5]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容