JS 手写题大汇总(一)--- 函数

142 阅读3分钟

最近在整理面试题,到了手写题这部分,也是重写了一遍加复习,也修改了一部分以前没有注意到的问题,我自认写的还是比较详尽的了,如果有异议的地方,欢迎大家一起讨论,一定要提出来!我不想面试才知道我的问题呜呜呜!

作为自身复习使用,所以并没有添加过多的注释,如果有不明白的可以私信或者评论哦。

这章只是纯粹的函数实现,并没有测试用例,有需求可以自行测试哈。

create

function myCreate(prototype) {
  const F = {}
  F.prototype = prototype
  return new F()
}

instance

function myInstance(left, right) {
  const prototype = right.prototype
  // let proto = left.__proto__
  let proto = Object.getPrototype(left)
  while (left) {
    if (left === right) return true
    left = Object.getPrototype(left)
  }
  return false
}

compose

function compose(fns) {
  if (fns.length === 0) return v => v
  if (fns.length === 1) return fns[0]()
  return fns.reduce((prev, cur) => (...args) => prev(cur(...agrs)))
}

new

function myNew(Ctor, ...args) {
  if (typeof Ctor !== 'function') {
    throw new TypeError('Error')
  }
  const instance = Object.create(Ctor.prototype)
  const res = Ctor.apply(instance, args)
  if (res && (typeof res === 'object' || typeof res === 'function')) {
    return res
  }
  return instance
}

getType

function getType(target) {
  if (target === null) return 'null'
  if (typeof target !== 'object') {
    return typeof target
  }
  return Object.prototype.toString.call(target).slice(8, -1)
}

call

Function.prototype.myCall = function(context, ...args) {
  if (typeof this !== 'function') throw new TypeError('Error')
  if (!context || (typeof context !== 'object' && typeof context !== 'function')) {
    context = window
  }
  const fn = Symbol()
  context[fn] = this
  const res = context.fn(...args)
  delete context[fn]
  return res
}

apply

Function.prototype.myApply = function(context, args) {
  if (typeof this !== 'function') throw new TypeError('Error')
  if (!Array.isArray(args)) throw new TypeError('Error')
  if (!context || (typeof context !== 'object' && typeof context !== 'function')) {
    context = window
  }
  const fn = Symbol()
  context[fn] = this
  const res = context.fn(...args)
  delete context[fn]
  return res
}

bind

Function.prototype.myBind = function(context, ...args1) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  if (!context || (typeof context !== 'object' && typeof context !== 'function')) {
    context = window
  }
  const fn = this
  const resFn = function(...args2) {
    return fn.call(this instance resFn ? this : context, ...args1, ...args2)
    // 注释为降级策略
    // if (this instance resFn) {
    //   this.fn = fn
    //   return this.fn(...args)
    // } else {
    //   context.fn = fn
    //   return context.fn(...args)
    // }
  }
  resFn.prototype = Object.create(this.prototype)  
  // function F() {}
  // F.prototype = this.prototype
  // resFn.prototype = new F()
  
  return resFn
}

flat

Array.prototype.myFlat = function(depth) {
  if (!Array.isArray(this)) throw new TypeError('Error')
  let res = this.slice()
  while (depth-- > 0) {
    res.reduce(
      (prev, cur) => Array.isArray(cur) ? [...prev, ...cur] : [...prev, cur]
    , [])
  }
  return res
}

setInterval

function mySetInterval(fn, timeout) {
  let timer = null
  function interval (){
    fn()
    timer = setTimeout(interval, timeout)
  }
  setTimeout(interval, timeout)
  return {
    clearInterval: () => {
      clearTimeout(timer)
      timer = null
    }
  }
}


function mySetInterval(fn, timeout) {
  let prev = Date.now()
  let timer = null
  function interval() {
    if (Date.now() - prev >= timeout) {
      fn()
      prev = Date.now()
    }
    timer = requestAnimationFrame(interval)
  }
  interval()
  return {
    myClearInterval: () => {
      cancelAnimationFrame(timer)
      timer = null
    }
  }
}

throttle

// 使用定时器
function throttle(fn, timeout) {
  let timer
  return function() {
    if (timer) return
    let context = this
    // fn(...args) 第一次就执行放这里
    timer = setTimeout(() => {
        fn.call(context, ...args)
        timer = null
    }, timerout)
  }
}

// 使用时间戳
function throttle(fn, timeout) {
  let prev = Date.now()
  return function(...args) {
    let context = this
    if (Date.now() - start >= timeout) {
      fn.call(context, ...args)
      start = Date.now()
    }
  }
}

debounce

function debounce(fn, timeout) {
  let timer
  return function(...args) {
    let context = this
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      fn.call(context, ...args)
    }, timeout)
  }
}

// requestAnimationFrame 实现防抖
function debounce(fn, timeout) {
  let prev = Date.now()

  function tick(...args) {
    let context = this
    if (Date.now() - satrt >= timeout) {
      fn.call(context, ...args)
    }
    timer = requestAnimationFrame(tick)
  }
  const timerID = {
    id: requestAnimationFrame(tick)
  }
  return timerID
}
// 使用 cancelAnimationFrame(timerID.id) 可销毁

// 首次执行的防抖函数
function immediateDebounce(fn, timeout, immediate) {
  let timer
  return function(...args) {
    const context = this
    if (timer) clearTimeout(timer)
    if (immediate) {
      let callNow = !timer
      timer = setTimeout(() => {
        timer = null
      }, timeout)
      if (callNow) fn.call(context, ...args)
    } else {
      timer = setTimeout(() => {
        fn.call(context, ...args)
      }, timeout)
    }
  }
}

curry

function curry(fn, ...args1) {
  let len = fn.length
  return function(...args2) {
    let args = [...args1, ...args2]
    if (args.length >= len) return fn(...args)
    return curry(fn, ...args)
  }
}

function add(...args1) {
  let args = [...args1]
  const fn = function(...args2) {
    args.push(...args2)
    return fn
  }
  fn.toString = function() {
    if (!args.length) return
    return args.reduce((prev, cur) => prev + cur, 0)
  }
  return fn
}

map

Array.prototype.myMap = function(fn, context) {
  if (!Array.isArray(this)) throw new TypeError('Error')
  let arr = new Array(this.length)
  for (let i = 0; i < this.length; i++) {
    if (i in this) {
      arr[i] = fn.call(context, this[i], i, this)
    }
  }
  return arr
}

reduce

Array.prototype.myReduce = function(fn, initialValue, context) {
  if (!Array.isArray(this)) throw new TypeError('Error')
  let arr = new Array(this.length)
  let i = initialValue ? 0 : 1
  let sum = initialValue || this[0]
  for (; i < this.length; i++) {
    sum = fn.call(context, sum, this[i], i, this)
  }
  return sum
}

iterator

const obj = { a: 1, b: 2 }

obj[Symbol.iterator] = function() {
  let keys = Object.keys(this)
  let ind = 0
  return {
    next() {
      if (ind < keys.length) return { done: false, value: obj[keys[ind++]] }
      return { done: true, value: undefined}
    }
  }
}

obj[Symbol.iterator] = function*() {
  const keys = Object.keys(this)
  for (let key of keys) {
    yield obj[key]
  }
}