前端面试笔记

  • HTTP报文结构?
    HTTP报文结构
  • ** HTTP缓存**
    缓存机制
  • 常见网络安全漏洞
    常见网络安全漏洞
  • 如何进行数组扁平化处理?
    将一个多维数组变为一个一维数组
    方法1:递归
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()接受一个数值参数,表示需要拉平的层级,Infinity表示无限层级的拉平。

[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() 函数:遍历数组,每次返回上一次的处理后的值。reduce(function(prev,next){}, initVal), initVal 为prev的初始值,不传initVal,prev的初始值为数组的第一个元素,next为第二个数组元素。

// reduce 每次的prev都是[],所以采用数组的concat()
const fn2 = arr =>  {
    return arr.reduce((prev,next) => { 
        return prev.concat(Array.isArray(next) ? fn2(next) : next)
    },[])
}
  • 防抖debounce
    触发高频事件后n秒内会执行一次,如果n秒内再次触发,则重新计算时间进行执行。
    即:触发高频事件每隔一定时间执行一次。
    场景:防抖常应用于用户进行搜索输入节约请求资源,滚动事件等
const debounce = (fn,time = 500) => {
   let timer = 0
   return function() {
      if(timer) clearTimeout(timer)
      timer = setTimeout(() => {
        fn.apply(this)
      }, time)
    }
  }
// 高频触发函数mousemove  
const box = document.getElementsByClassName('box')[0]
const fn4 = () => console.log('触发函数')
box.addEventListener('mousemove', debounce(fn4))
  • 节流throttle
    n秒内触发高频事件只会执行一次, 所以节流会稀释函数的执行频率
    场景:节流常应用于防重复点击
const throttle = (fn, time = 100) => {
    let isFinished = false
    return function() {
      if(isFinished) {
            clearTimeout(timer)
            return 
      }
      timer = setTimeout(() => {
        fn.apply(this)
        isFinished = true
      }, time)
  }
}
// 防重复点击
const box = document.getElementsByClassName('box')[0]
const fn4 = () => console.log('触发函数')
box.addEventListener('click', throttle(fn4))
  • arguments.callee
    在函数内部,有两个特殊的对象:arguments 和 this。其中, arguments 的主要用途是保存函数参数, 但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。
    场景:使用递归的过程中会根据条件来调用函数本身,这样会导致一个问题,函数内部存在一个我们自定义的函数名,不利于再次封装。
// 使用函数名
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
  }
}
  • 函数珂里化
    指的是将一个接受多个参数的函数变为接受一个参数,返回一个函数的固定形式,便于再次调用。
// 求和: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. 所有引用类型(函数,数组,对象)都拥有proto属性(隐式原型)
  2. 所有函数拥有prototype属性(显式原型)(仅限函数)
  3. 原型链的实现:查找属性,如果本身没有,则会去__ proto __ 中查找,也就是构造函数的显式原型中查找,如果构造函数中也没有该属性,因为构造函数也是对象,也有 __ proto __,那么会去它的显式原型中查找,一直到null,如果没有则返回undefined。
  4. 构造函数都有一个prototype属性指向原型对象原型对象通过constructor属性指向构造函数本身。
    实例对象都有一个__ proto __属性指向原型对象
  5. 实例对象的__ proto __指向构造函数的prototype,从而实现继承。
    prototype对象相当于特定类型所有实例对象都可以访问的公共容器
  6. 继承:
    (1)原型链继承
    (2)call和apply。区别call(this,p1,p2,p3,...),apply(this.[...params])第二个开始为参数集合
    (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 操作符的实现
    思路:新创建一个空对象,新对象原型赋值构造函数的原型,执行构造函数并绑定this到新对象。(新对象拥有构造函数及原型上的属性和方法)
function _MyNew(obj) {
    // 创建一个空对象
    let newObj = Object.create()
    // 设置空对象的原型
    newObj.__proto__ = obj.prototype
    // 使用apply绑定this,执行构造函数
    const res = obj.apply(newObj)
    const isObject = typeof res === 'object' && res !== null;
    const isFunction = typeof res === 'function';
    return isObject || isFunction ? res : obj;
}
  • 宏任务与微任务
    Javascript 是单线程脚本语言。分为同步任务(立即执行)、异步任务(不会立即执行),异步任务分为宏任务、微任务,异步任务进过事件注册模块将回调添加到任务队列进行处理。
    (1)宏任务
    常见的有:
    setTimeout
    setInterval
    requireAnimationFrame请求动画帧(浏览器独有)
    UI render(浏览器独有)
    setImmediate(node独有)
    I/O
    (2)微任务
    常见的有:
    Promise.then()
    Process.nextTick(node独有)
    Object.observe
    MutationObserve
    (1)当一个异步任务进入栈的时候,主线程会判断是同步还是异步任务,如果是同步任务,则立即执行;如果是异步任务,则将该任务交给异步处理模块处理,当异步处理完到触发条件的时候,根据任务的类型,将回调压入队列之中。如果是宏任务,则新增一个宏任务,任务队列中的宏任务可以有多个;如果是微任务,则直接压入微任务队列。
    (2)当两个宏任务队列的时候,第一个宏任务队列中有一个微任务:即当在执行宏任务的时候,遇到微任务则将微任务加入微任务队列,最后剩下一个微任务和一个宏任务,此时会先执行微任务,再执行宏任务
    (3)宏任务与微任务在浏览器和node中的执行有所差异
    浏览器:执行宏任务,然后执行宏任务对应的微任务,重复进行。由于执行栈的入口为script,而全局任务为宏任务,所以当栈为空的时候,同步任务执行完毕,会先执行微任务队列。微任务执行完毕,会读取宏任务队列中最前的任务,执行宏任务的过程中,如果遇到微任务,则依次加入微任务队列,栈空后,再次读取微任务,依次类推。
    node:执行完全部的宏任务,然后执行微任务
      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
转 https://blog.csdn.net/qq_42833001/article/details/87252890

  • 渲染几万条数据不卡住页面借助请求动画帧requestAnimationFrame
/**
        功能: 匀速滚动到页面顶部
        @dom: 事件源DOM,
        @speed: 总份数
      */
      function scrollTopHandler(dom, speed = 500) {
        // 滚动的高度
        let doc_scrollTop
        let i = 0
        // requestAnimationFrame 的执行函数
        const gotoTop = function() {
          // 将滚动高度分为1000份, 每份为unit的高度
          const unit = doc_scrollTop / speed
          // 每次执行10份unit的距离
          i += 10
          // 计算每次滚动的高度
          const scrollTop = doc_scrollTop - i * unit > 0 ? doc_scrollTop - i * unit : 0
          document.documentElement.scrollTop = scrollTop
          // 判断执行分数i小于speed 总分数,则继续执行
          if (i <= speed) {
            window.requestAnimationFrame(gotoTop)
          } else {
            // 重置
            i = 0
          }
        }
        // 点击事件监听
        const handler = function() {
          doc_scrollTop = document.documentElement.scrollTop
          window.requestAnimationFrame(gotoTop)
        }
        dom.addEventListener('click',handler,false)
      }
      // 调用方法
      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
      }

双指针-快速排序
思想:来自于快速排序,在一个有序的数组(从小到大)中最左边一定是最小值,最右边是最大值。我们可将最小值与最大值相加与目标值进行比较,如果两数之和大于目标值,我们就让最大值小一点(读取第二个最大值),如果两数之和小于目标值,我们就让最小值大一点(读取第二个最小值),如果两数之和刚好等于目标值,保存最大值,最小值,并且让最大值小一点,最小值大一点。需要注意的是前提条件是数组必须有序!

  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]

你可能感兴趣的:(前端面试笔记)