死磕javascript的手写面试题

2021-4-15    前端达人

1.实现lodash的_.get方法

function _getValue(target, valuePath, defalutVal) {
  let valueType = Object.prototype.toString.call(target)
  console.log(valueType)
  // if (valueType == "[object Array]") {
    let paths = valuePath.replace(/\[(\d+)\]/, `.$1`).split('.')
    let result = target
    for(const path of paths){
      result = Object(result)[path]
      if(result == undefined){
        return defalutVal
      }
    }
    return result
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
测试:
let obj = {
  a:{
    b:[
      {
        c:2
      }
    ]
  }
}

console.log(_getValue(obj, 'a.b[0].c')) //2 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2.写一个函数判断两个变量是否相等

function isEqual(res1, res2) {
  let a = getTypeOf(res1)
  let b = getTypeOf(res2)
  if(a !== b){
    return false
  }else if(a === 'base'){
    console.log('base',res1,res2)
    return res1 === res2
  } else if(a === 'array'){
    if(res1.length !== res2.length){
      console.log('array',res1,res2)
      return false
    }else{
      //遍历数组的值比较
      for(let i =0;i<res1.length;i++){
        if(!isEqual(res1[i],res2[i])){
          console.log('array',res1[i],res2[i])
          return false
        }
      }
      return true
    }
    return true
  }else if(a === 'object'){
    let ak = Object.keys(a)
    let bk = Object.keys(b)
    if(ak.length !== bk.length){
      return false
    }else{
      for(let o in res1){
        console.log(res1[o])
        if(!isEqual(res1[o],res2[o])){
          console.log('object',res1[o],res2[o])
          return false
        }
      }
      return true
    } 
  }else if(a === 'null' || a === 'undefined'){
    console.log('null')
    return true
  }else if(a === 'function'){
    console.log('function')
    return a === b
  }
}

function getTypeOf(res) {
  let type = Object.prototype.toString.call(res)
  switch (type) {
    case "[object Array]":
      return 'array'
    case "[object Object]":
      return 'object'
    case "[object Null]":
      return 'null'
    case "[object Undefined]":
      return 'undefined'
    case "[object Number]"||"[object String]"||"[object Boolean]":
      return 'base'
    case "[object Function]":
      return 'function'
    default:
      return 'typeError'
  }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
测试:
let a = {
  a:20,
  b:{
    c:30,
    d:[1,2,3]
  }
}
let b = {
  a:20,
  b:{
    c:30,
    d:[1,2,3]
  }
}
console.log(isEqual(a,b)) //true 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3.数组扁平化的方法

function _flat(arr){
  let result = []
  for(let i = 0;i<arr.length;i++){
    if(Array.isArray(arr[i])){
      result = result.concat(_flat(arr[i]))
    }else{
      result.push(arr[i])
    }
  }
  return result;
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
let arr = [1,2,[3,4,[5,6]]]
_flat(arr) //[1,2,3,4,5,6] 
  • 1
  • 2
//es6
function _flat2(arr){
  while(arr.some(item=>Array.isArray(item))){
    arr = [].concat(...arr)
  }
  return arr
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
let arr = [1,2,[3,4,[5,6]]]
_flat2(arr) //[1,2,3,4,5,6] 
  • 1
  • 2

4.深克隆

简单深克隆,不考虑内置对象和函数

function deepClone(obj){
  if(typeof obj !== 'object') return
  let newObj = obj instanceof Array?[]:{}
  for(let key in obj){
      if(obj.hasOwnProperty(key)){
          newObj[key] = typeof obj[key] === 'object'?deepClone(obj[key]):obj[key]
      }
  }
  return newObj
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

复杂版深度克隆 考虑内置对象 比如date regexp 函数 以及对象的循环引用的问题

const isObject = (target) => typeof target === "object"&& target !== null;

function deepClone2(target, map = new WeakMap()) {
  console.log(target)
    if (map.get(target)) {
        return target;
    }
    // 获取当前值的构造函数:获取它的类型
    let constructor = target.constructor;
    // 检测当前对象target是否与正则、日期格式对象匹配
    if (/^(RegExp|Date)$/i.test(constructor.name)) {
        // 创建一个新的特殊对象(正则类/日期类)的实例
        return new constructor(target);  
    }
    if (isObject(target)) {
        map.set(target, true);  // 为循环引用的对象做标记
        const cloneTarget = Array.isArray(target) ? [] : {};
        for (let prop in target) {
            if (target.hasOwnProperty(prop)) {
                cloneTarget[prop] = deepClone(target[prop], map);
            }
        }
        return cloneTarget;
    } else {
        return target;
    }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

5.数组去重

filter去重

function _unique(arr){
  return arr.filter((item,index,array)=>{
    return array.indexOf(item) === index
  })
} 
  • 1
  • 2
  • 3
  • 4
  • 5

es6 Set

function _unique2(arr){
  return [...new Set(arr)]
} 
  • 1
  • 2
  • 3

includes

function _unique3(arr){
  let newArr = []
  arr.forEach(item => {
      if(!newArr.includes(item)){
        newArr.push(item)
      }
  });
  return newArr
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

双层for循环

function _unique4(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
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

indexof

function _unique5(arr){
  let newArr = []
  for(let i = 0;i<arr.length;i++){
    if(newArr.indexOf(arr[i] === -1){
      newArr.push(arr[i])
    })
  }
  return newArr
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

6.判断数据的类型

function _typeOf(obj){
  let res = Object.prototype.toString.call(obj).split(' ')[1]
  let mold = res.substring(0,res.length-1).toLowerCase()
  return mold
} 
  • 1
  • 2
  • 3
  • 4
  • 5
_typeOf(5) //number
_typeOf('5') //string 
  • 1
  • 2

7.解析url参数为对象

function getParamsObj(params){
  let paramsStr = params.replace(/^.+\?(.+)/,"$1")
  let paramsArr = paramsStr.split('&')
  let paramsObj = {}

  for(let [key,value] of paramsArr.entries()){
      if(/=/.test(value)){
          let valArr = value.split('=')
          val = decodeURIComponent(valArr[1]) //解码
          val = /^\d+$/.test(val)?parseFloat(val):val //判断是不是数字
          if(paramsObj.hasOwnProperty(valArr[0])){
              paramsObj[valArr[0]] = [].concat(paramsObj[valArr[0]],val)
          }else{
              paramsObj[valArr[0]] = val
          }
      }  

  }
  return paramsObj
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

8.函数柯里化

//从一次传入多个参数  编程多次调用每次传入一个参数
function add(a, b, c, d, e) {
  return a + b + c + d + e
}

function curry(fn) {
   let dFn = (...args)=>{
     if(args.length == fn.length) return fn(...args)
     return (...arg)=>{
       return dFn(...args,...arg)
     }
   }
   return dFn
}
let addCurry = curry(add)
addCurry(1,2,3)(2)(3) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

9.图片懒加载

//添加了两个功能
// 图片加载完成后 移除事件监听
// 加载完的图片从imgList中移除
let imgList = [...document.querySelectorAll('img')]
let length = imgList.length

const imgLazyLoad = function () {
  let count = 0
  let deleteIndexList = []
  imgList.forEach((img, index) => {
    let rect = img.getBoundingClientRect() 
    //获取元素到视图的距离 top元素上边到视图上边的距离 left元素左边到视图左边的距离  right... bottom...
    if (rect.top < window.innerHeight) {
      // img.src = img.dataset.src
      img.src = img.getAttribute('data-src')
      deleteIndexList.push(index)
      count++
      if (count === length) {
        document.removeEventListener('scroll', imgLazyLoad)
      }
    }
  })
  imgList = imgList.filter((img, index) => !deleteIndexList.includes(index))
}
imgLazyLoad()

document.addEventListener('scroll', imgLazyLoad) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

图片懒加载:https://juejin.cn/post/6844903856489365518#heading-19

10节流防抖

函数防抖 触发高频事件 事件在n后执行,如果n秒钟重复执行了 则时间重置

//简易版
function debounce(func,wait){
  let timer; 
  return function(){
    let context = this;
    let args = arguments;
    console.log(timer)
    clearTimeout(timer)
    timer = setTimeout(function(){
      func.apply(context,args)
    },wait)
  }

}
let btn = document.querySelector('button');
function aa(){
  console.log(111)
}
btn.onclick = debounce(aa,2000) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
// 复杂版
// 1.取消防抖  
// 2.立即执行功能(点击之后立即执行函数  但是 wait时间之后在点击才能在立即执行)  
// 3.函数可能有返回值
function debounce(func,wait,immediate){
  let timer,result;

  const debounce = function () {
    const context = this
    const args = arguments

    if(timer) clearTimeout(timer)
    if(immediate){
      console.log(timer)
      var callNow = !timer
      timer = setTimeout(function () {
          timer =null
      },wait)
      if(callNow) result = func.apply(context,args)
    }else{
      timer = setTimeout(function (params) {
        result = func.apply(context,args)
      },wait)
    }
    return result
  }

  debounce.cance = function () {
    clearTimeout(timer)
    timer=null
  }

  return debounce

}

let btn = document.querySelector('button');
function aa(){
  console.log(111)
}
btn.onclick = debounce(aa,2000,true)``` 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

函数节流 触发高频事件 且n秒只执行一次

//使用时间戳
function  throttle(func,wait) {
  var context,args;
  var previous = 0

  return function () {
    context = this;
    args = arguments;
    let nowDate = +new Date()
    if(nowDate-previous>wait){
      func.apply(context,arguments)
      previous = nowDate
    }
  }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
//定时器
function throttle(func,wait) {
  var context,args;
  var timer;
  return function(){
    context = this;
    args = arguments;
    if(!timer){
      timer = setTimeout(function () {
        timer = null;
        func.apply(context,args)
      },wait)
    }
  }

} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
//组合版 options.leading 为true 立即执行一次 options.trailing为true  结束之后执行一次 默认为true
function throttle(func, wait ,options = {}) {
  var context, args, timer,result;
  var previous = 0;

  var later = function () {
    previous = options.leading === false ? 0 : new Date().getTime();
    timer = null;
    func.apply(context, args)
    if (!timer) context = args = null;
  }

  var throttle = function () {
    var now = new Date().getTime()
    if (!previous && options.leading === false) previous = now;
    context = this;
    args = arguments;

    //下次触发 func 剩余的时间
    var remaining = wait - (now - previous);
    if (remaining <= 0 || remaining > wait) {
      // if (timer) {
      //   clearTimeout(timer);
      //   timer = null;
      // }
      previous = now;
      func.apply(context, args);
      if (!timer) context = args = null;
    } else if (!timer&& options.trailing !== false) {
      timer = setTimeout(later, remaining);
    }
  }

  throttled.cancel = function() {
    clearTimeout(timer);
    previous = 0;
    timer = null;
  }

  return throttle
}

function aa(e) {
  console.log(111)
  console.log(e)
}

let btn = document.querySelector('button');
btn.onclick = throttle(aa, 2000,{
  leading:false,
  trailing:true 

})

转自:csdn论坛 作者:Selfimpr欧

分享本文至:

日历

链接

blogger

蓝蓝 http://www.lanlanwork.com

存档