JS深拷贝/闭包/防抖/节流-面试必考

49 阅读3分钟

深拷贝

递归拷贝对象或数组,新建一个对象或数组,对原对象或数组的每个属性进行复制。

  • 深拷贝过的对象或者数组进行改变之后,原来的origin对象或数组不会发生变化。
  • 如果没有深拷贝,比如let obj= originObj,那么改变obj的同时,originObj也会发生改变
function deepclone(obj) {
    //如果参数不是数组和对象,或者参数是null的时候,就直接返回
    if (typeof (obj) !== 'object' || obj === null) return obj
    // 如果说参数是时间对象,就返回时间
    if (obj instanceof Date) return new Date(obj)
    //如果参数是正则,就返回正则
    if (obj instanceof RegExp) return new RegExp(obj)

    // 如果参数是数组,就初始化数组,如果是对象,就初始化为对象
    let newObj = Array.isArray(obj) ? [] : {}

    //遍历对象所有的属性
    for (let prop in obj) {
        //检查所有对象的自有属性
        if (obj.hasOwnProperty(prop)) {
            newObj[prop] = deepclone(obj[prop])
        }
    }
    return newObj
}

闭包

  • 闭包允许函数“记住”其词法作用域中的变量,即使该函数是在当前词法作用域之外执行。
  • 这意味着,闭包可以保存和管理某些方法所需的状态变量
  • 例如防抖和节流上次执行的时间戳、定时器标识等。这些状态变量对于防抖和节流的逻辑至关重要。
function closure() {
    let count = 0
    return function(){
        count ++
        return count
    }
}

let a = closure()
console.log(a())
console.log(a())
console.log(a())


// 为什么不把count放在外层呢?是因为放在外层别的地方可以随意更改count的值,
// 下面这种做法就不是闭包了,
// 闭包是可以吧count私有化,只有在调用closure里面返回的inner函数,才可以修改count的值,
// 并且count的值也不会被销毁
let count = 0
function closure() {
    return function(){
        count ++
        return count
    }
}

防抖

在事件被触发后,只有在一定时间间隔内没有再触发该事件,才会执行函数

  • 像感应门,如果有人走进去,就清除定时器,然后从头开始计时,
  • 直到最后一次超过某个时间间隔,门彻底关上之后,才可请求方法,再次打开
// 像感应门,如果有人走进去,就清除定时器,然后从头开始计时,
// 直到最后一次超过某个时间间隔,门彻底关上之后,才可请求方法
function debounce(fn, delay, agus) {
    let timer = null 
    return function() {
        clearTimeout(timer)
        let context = this
        timer = setTimeout (function(){
            fn.apply(context, agus)
        }, delay)
    }
}

节流

在一定时间间隔内,只允许函数执行一次。

  • 像resize,scroll这种高频动作,不能一直执行函数
  • 要有一定的时间间隔再次执行函数,
  • 所以在执行函数的过程中,如果再次执行的话,就阻止其执行
//像resize,scroll这种高频动作,不能一直执行函数
// 所以要有一定的时间间隔再次执行函数

//方法一:用定时器
function throttle(fn, interval, agus) {
    let timer = null
    return function(){
      //如果timer不等于null,那么就return,防止再次调用执行函数
      if (timer !== null) return
      let context = this
      timer = setTimeout(function(){
          fn.apply(context, agus)
          timer = null
      }, interval)
    }
}

//方法二:用Date来做
function throttle1(fn, interval, agus){
    let lastTime = 0
    return function(){
     let newTime = new Date()
     // 如果新时间-旧时间的间隔小于规定的interval,那么就return函数
     if (newTime - lastTime< interval) return
     lastTime = newTime
     fn.apply(this, agus)
    }
}