DAY5 手写js系列(二)

# DAY5 手写js系列(二)

# 7. 用setTimeout实现setInterval

/* 
题目说明:setinterval 用来实现循环定时调用 可能会存在一定的问题 能用 settimeout 解决吗
*/
function mySetInterval(fn, delay) {
  let timer = null
  const interval = () => {
    fn()
    timer = setTimeout(interval, delay)
  }
  setTimeout(interval, delay)
  return {
    clear: () => {
      clearTimeout(timer)
    }
  }
}

# 8. 用setInterval实现setTimeout

function mySetTimeout(fn, delay) {
  let timer = null
  timer = setInterval(() => {
    fn()
    clearInterval(timer)
  }, delay)

  return {
    clear: () => {
      clearInterval(timer)
    }
  }
}

# 9. 实现一个compose函数

/* 第一种实现 */
function compose1(...fn) {
  return function (x) {
    for (let i = 0; i < fn.length; i++) {
      x = fn[i](x)
    }
    return x
  }
}

/* 第二种实现:使用reduce */
function compose2(...fn) {
  if (fn.length === 0) return (x) => x
  if (fn.length === 1) {
    return fn[0]
  }
  return function (x) {
    return fn.reduce((pre, cur) => {
      return cur(pre(x))
    })
  }
}

# 10. 实现一个科里化函数

function currying(fn, ...args1) {
  let len = fn.length
  let allArgs = [...args1]

  res = (...args2) => {
    allArgs = [...allArgs, ...args2]
    // 判断参数个数是否等于fn需要的参数个数
    if (len === allArgs.length) {
      return fn(...allArgs)
    } else {
      // 不相等继续返回函数
      return res
    }
  }

  return res
}

# 11. 实现一个LRU缓存函数

class LRUCache {
  constructor(size) {
    this.size = size
    this.cache = new Map()
  }

  get(key) {
    // 找不到该键,返回-1
    if (!this.cache.has(key)) {
      return -1
    } else {
      const val = this.cache.get(key)
      // 删除旧的键,更新其位置
      this.cache.delete(key)
      this.cache.set(key, val)
      // 返回键值
      return val
    }
  }

  put(key, val) {
    const hasKey = this.cache.has(key)
    // 存在该键,删除旧键
    if (hasKey) {
      this.cache.delete(key)
    }
    // 设置新键
    this.cache.set(key, val)

    // 设置完键,检查是否长度越界
    if (this.cache.size > this.size) {
      // 删除最久未使用的键
      // keys()获得所有键的iterator,next()获得最早设置的键的对象,通过value获得键
      this.cache.delete(this.cache.keys().next().value)
    }
  }
}


/* 测试 */
cache = new LRUCache(2)
console.log(cache.put(1, 1))
console.log(cache.put(2, 2))
console.log(cache.get(1))
console.log(cache.put(3, 3)) //2作废
console.log(cache.get(2)) // -1
console.log(cache.put(4, 4)) //1作废
console.log(cache.get(1)) //-1
console.log(cache.get(3))
console.log(cache.get(4))

# 12. 简单实现 发布订阅模式

题目描述:实现一个发布订阅模式拥有on emit once off方法

/* 
题目描述:实现一个发布订阅模式拥有on emit once off方法
*/

class EventEmitter {
  constructor() {
    // 事件调度中心
    this.cache = {}
  }

  // 订阅事件
  on(name, fn) {
    const tasks = this.cache[name]
    if (tasks) {
      this.cache[name].push(fn)
    } else {
      this.cache[name] = [fn]
    }
  }

  // 触发事件
  emit(name, ...args) {
    // 复制一份回调队列,防止回调里继续on导致死循环
    const tasks = this.cache[name].slice()
    if (tasks) {
      for (let fn of tasks) {
        fn(...args)
      }
    }
  }

  // 解除事件订阅
  off(name, fn) {
    const tasks = this.cache[name]
    if (tasks) {
      const index = tasks.findIndex((item) => item === fn)
      if (index >= 0) {
        // splice修改原数组,slice不修改原数组
        this.cache[name].splice(index, 1)
      }
    }
  }

  // 单次事件
  once(name, fn) {
    // 这里要保存以下this,或者使用箭头函数
    let that = this
    function cb(...args) {
      fn(...args)
      that.off(name, cb)
    }
    this.on(name, cb)
  }
}

module.exports = {
  EventEmitter
}