前端面试常见的手撕题目

226 阅读3分钟

手写JS方法

Promise

Promise.all

function MyPromiseAll(arr){
  return new Promise((resolve,reject)=>{
    let res=[]
    for(let i=0;i<arr.length;i++){
      Promise.resolve(arr[i])
      .then((data)=>{
        res[i]=data
        if(!res.includes(undefined)&&res.length===arr.length){
          resolve(res)
        }
      })
      .catch(err=>{
        reject(err)
      })
    }
  })
}

Promise.race

function  MyPromiseRace(arr){
  return new Promise((resolve,reject)=>{
    for(let i=0;i<arr.length;i++){
      Promise.resolve(arr[i])
      .then(data=>{
        resolve(data)
      })
      .catch(err=>{
        resolve(err)
      })
    }
  })
}

深拷贝

const setTag='[object Set]'
const mapTag='[object Map]'
const funTag='[object Function]'
const dateTag='[object Date]'
const regxTag='[object RegExp]'
const symbolTag='[object Symbol]'
const isObject = (target) => {
  const type = typeof target
  return target !== null && (type === 'object' || type === 'function')
}
const getType=(obj)=>{
  return Object.prototype.toString.call(obj)
}
const getInit=(obj)=>{
  const con=obj.constructor
  return new con()
}
function deepClone(target, map = new Map()) {
  //原始类型直接返回
  if (!isObject(target)) return target
  // let clone = target instanceof Array ? [] : {}
  //获取克隆模板
  let clone = getInit(target)
  //防止循环引用
  if (map.get(target)) {
    return map.get(target)
  }
  map.set(target, clone)
  const type=getType(target)
  //克隆set
  if(type === setTag){
    target.forEach((item)=>{
      clone.add(deepClone(item,map))
    })
    return  clone
  }
  //克隆map
  if(type === mapTag){
    target.forEach((value,key)=>{
      clone.set(key,deepClone(value,map))
    })
    return clone
  }
  //克隆函数
  if(type === funTag ){
    return target
  }
  //克隆date
  if(type===dateTag){
    return new target.constructor(target)
  }
  //克隆regex
  if(type === regxTag){
    return new target.constructor(target.source,/\w*$/.exec(target))
  }
  //克隆symbol---bug!作为基本类型返回,导致克隆对象和源对象Symbol相等
  if(type === symbolTag){
    //有待商榷
  }
  //对象和数组
  for (const key in target) {
    clone[key] = deepClone(target[key], map)
  }
  return clone
}

AJAX

bind

Function.prototype.myBind=(obj,...args)=>{
  let that=this
  return (...newArgs)=>{
    that.apply(obj,[...args,...newArgs])
  }
}
Function.prototype.myBind= (obj,...args)=>{
  obj.fn=this
  return (...newArgs)=>{
    obj.fn(...newArgs,...args)
    delete obj.fn
  }
}

apply

Function.prototype.myApply=(obj,args)=>{
  obj.fn=this
  let rs=obj.fn(...args)
  delete obj.fn
  return rs
}

call

Function.prototype.myCall=(obj,...args)=>{
  obj.fn=this
  let rs=obj.fn(...args)
  delete obj.fn
  return rs
}

reduce

Array.prototype.myReduce = function (cb, initVal) {
  let pre = initVal ? initVal : this.splice(0, 1)[0]
  for (let i = 0; i < this.length; i++) {
    pre = cb(pre, this[i], i, this)
  }
  return pre
}

instanceof

const myInstanceof=(left,right)=>{
  let proto=right.prototype
  let _left=left.__proto__
  while(_left){
    if(_left===proto) return true
    _left=_left.__proto__
  }
  return  false
}
console.log(myInstanceof([],Object))

防抖节流

// 防抖:连续触发多次,最后一次执行
function debounce(fn, delay) {
  let timer
  return () => {
    if (timer) {
      clearTimeout(timer)
    }
    timer=setTimeout(() => {
      fn()
    }, delay)
  }
}
const fn = () => {
  console.log('hello')
}
window.addEventListener('click', debounce(fn, 1000))//测试
//节流:一段之间内连续触发,只执行一次
function throttle(fn,time){
  let oldTime=Date.now()
  return ()=>{
    let newTime=Date.now()
    if(newTime-oldTime>=time){
      fn()
      oldTime=newTime
    }
  }
}
const fn=()=>{
  console.log('hello')
}
window.addEventListener('click',throttle(fn,1000))//测试

柯里化

//定长参数
function curry(fn,...args){
  if(args.length>=fn.length){
    return fn(...args)
  }else {
    return (..._args)=> curry(fn,...args,..._args)
  }
}
function add(a,b,c){
  return a+b+c
}
const curryAdd=curry(add)
console.log(curryAdd(1,2,3))//6
console.log(curryAdd(1)(2)(3))//6
//不定长参数
function curry(fn,...args){
  function _curry(..._args){
    return curry(fn,...args,..._args)
  }
  _curry.toString=function(){
    return  fn(...args)
  }
  return _curry
}
function dynamicAdd() {
  return [...arguments].reduce((prev, curr) => prev + curr)
}
let add = curry(dynamicAdd);
alert(add(1)(2)(3)(4))//console.log输出函数体,需要alert

发布订阅

class EventEmitter{
  constructor() {
    this.events={}
  }
  on(eventName,fn){
    if(!this.events[eventName]){
      this.events[eventName]=[fn]
    }else{
      this.events[eventName].push(fn)
    }
  }
  emit(eventName,...args){
    this.events[eventName].forEach(fn=>fn.apply(this,args))
  }
  off(eventName,fn){
    this.events[eventName]=this.events[eventName].filter(item=>item!==fn)
  }
  once(eventName,fn){
    let that=this
    function func(){
      fn()
      that.off(eventName,func)
    }
    this.on(eventName,func)
  }
}

setTimeout实现setInterval

function myInterval(fn,wait){
  setTimeout(()=>{
    fn()
    myInterval(fn,wait)
  },wait)
}

力扣高频题

  • 11 盛水最多容器(42)
  • 15 三数之和为0
  • 20 判断括号有效
  • 21 合并两个有序链表
  • 25 K个一组反转链表
  • 42 接雨水
  • 46 全排列(77)
  • 50 快速幂
  • 53 最大子数组和
  • 66 加一
  • 77 组合
  • 98 验证二叉搜索数
  • 110 平衡二叉树
  • 134 加油站
  • 136 只出现一次的数字
  • 137 只出现一次的数字2
  • 146 LRU缓存
  • 206 反转链表
  • 215 数组中的第K个最大元素
  • 232 用栈实现队列
  • 300 最长递增序列、最长公共子串
  • 动态规划、二分查找
  • 328 奇偶链表
  • 343 整数拆分
  • 382 链表随机结点(水塘抽样)
  • 415 大数加法
  • 470 用Rand7实现Rand10 1.27
  • 496 下一个更大元素 1.27
  • 704 二分查找
  • 860 柠檬水找零

简单算法题

斐波那契

function fib(n) {
  if(n<=2) return 1
  return  fib(n-1)+fib(n-2)+fib(n-3)
}
// 拓展---三数之和---尾递归优化
//1 1 1 3 5 9 17
function fib(n,a=1,b=1,sum=1){
  if (n===3) return sum
  return fib(n-1,b,sum,a+b+sum)
}

数组拍平

function flatten(arr){
  let res=[]
  for(let i=0;i<arr.length;i++){
    if(Array.isArray(arr[i])){
      res=res.concat(flatten(arr[i]))
    }else{
      res.push(arr[i])
    }
  }
  return res
}

数组去重

function  fn(arr){
  return Array(...new Set(arr))
}
function fn2(arr){
  return arr.filter((value,index)=> arr.indexOf(value)===index)
}
function fn3(arr){
  return arr.reduce((acc,cur)=>{
    return acc.includes(cur)?acc:acc.concat(cur)
  },[])
}
function fn4(arr){
  for(let i=0;i<arr.length;i++){
    for(let j=i+1;j<arr.length;j++){
      if(arr[i]===arr[j]){
        arr.splice(j,1)
        j--
      }
    }
  }
  return arr
}
let arr=[2,3,4,5,6,3,3,3,3,4,5,6,4]
console.log(fn(arr))
console.log(fn2(arr))
console.log(fn3(arr))
console.log(fn4(arr))

找出数组中重复元素

两个无序数组合并为有序数组

环形链表找入口节点

质数(素数)

//返回一个数的所有质因数
function primeNumber(n){
  let k=2
  let rs=[]
  while(k<=n){
    if(k===n){
      rs.push(n)
      break;
    }else{
      if(n%k===0){
        rs.push(k)
        n=n/k
      }else{
        k++
      }
    }
  }
  return rs
}
//判断一个数为质数
function isPrime(n){
  if(n<2) return false
  if(n%2===0) return  false
  for(let k=3;k<=Math.sqrt(n);k+=2){
    if(n%k===0) return false
  }
  return true
}

排序

快排

//借助额外空间
function quickSort(arr) {
  if (arr.length <= 1) return arr
  let left = [], right = []
  let pivot = arr.splice(arr.length >> 1, 1)[0]
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] <= pivot) left.push(arr[i])
    else right.push(arr[i])
  }
  return quickSort(left).concat(pivot, quickSort(right))
}
//原地转换,不使用额外空间
function quickSort(arr, left, right) {
  if(left>=right) return
  let i = left, j = right;
  let pivot = arr[i]
  while(i<j){
    while(i<j && arr[j]>=pivot) j--
    if(i<j){
      arr[i]=arr[j]
      i++
    }
    while (i < j && arr[i] < pivot) i++
    if(i<j){
      arr[j]=arr[i]
      j--
    }
  }
  arr[i]=pivot
  quickSort(arr,left,i-1)
  quickSort(arr,i+1,right)
}

冒泡排序

function bubbleSort(arr){
  for(let i=0;i<arr.length-1;i++){
    for(let j=0;j<arr.length-1-i;j++){
      if(arr[j]>arr[j+1]){
        let tmp=arr[j+1]
        arr[j+1]=arr[j]
        arr[j]=tmp
      }
    }
  }
}

插入排序

function insertSort(arr) {
  for (let i = 1; i < arr.length; i++) {
    let j = i - 1	
    const tmp = arr[i]
    while (j >= 0 && arr[j] > tmp) {
      arr[j + 1] = arr[j]// 移动
      j--
    }
    // 放入合适位置
    arr[j + 1] = tmp
  }
  return arr
}
function insertSort(arr){
  for(let i=0;i<arr.length-1;i++){
    for(let j=i+1;j>0;j--){
      if(arr[j]<arr[j-1]){
        let tmp=arr[j-1]
        arr[j-1]=arr[j]
        arr[j]=tmp
      }else{
        break
      }
    }
  }
}

选择排序

function selectSort(arr) {
  for(let i=0;i<arr.length;i++){
    let minIndex=i;
    for(let j=i+1;j<arr.length;j++){
      if(arr[j]<arr[minIndex]) minIndex=j
    }
    let tmp=arr[i]
    arr[i]=arr[minIndex]
    arr[minIndex]=tmp
  }
}

希尔排序

堆排序

其他

返回两数的最大公约数

const gcd = (a, b) => {
  if (b === 0) {
    return a;
  }
  return gcd(b, a % b);
}

查找缺失的数📎missingNumber.js

计算圆周率📎pi.js

下划线驼峰互转📎camel.js

数组转对象📎array2Object.js