Skip to content
大纲

手撕代码

手写深拷贝

前情提要

  • 简单版 : JSON.parse(JSON.stringify(...))
  • 正常版 : 如下
js
function cloneDeep(target) {
  // 1. 如果是原始类型或函数直接返回
  if (typeof target !== 'object' || target == null) {
    return target
  }
  // 2. 判断是数组还是对象
  const res = Array.isArray(target) ? [] : {}
  for (const key in target) {
    if (target.hasOwnProperty(key)) {
      // 3. 递归
      res[key] = cloneDeep(target[key])
    }
  }
  return res
}

// 1. 还可以使用 JSON.parse(JSON.stringify(...))
// 2. 但是不能包含不安全的 JSON 值
// 3. 如 undefined, symbol, function, bigint, Infinity, NaN

手写 call()、apply()、bind() 函数

前情提要

  1. call、apply、bind 函数都是强制绑定 this 的函数
  2. bind 返回的是一个函数, 且可以进行函数柯里化
  3. 而 call 与 apply 会立即执行函数, 只不过参数方式不一样
    • apply 的剩余参数是数组
    • call 的剩余参数的一个一个的
  4. 原生 call、apply 函数对原始数据类型会 new Object(contextArg), 对于 null 或 undefiend this 会绑定上 window 或 global
js
Function.prototype.apply_polyfill_1 = function (contextArg, args) {
  if (contextArg == null) contextArg = globalThis
  if (typeof contextArg !== 'object') contextArg = new Object(contextArg)
  const TEMP = Symbol()
  contextArg[TEMP] = this
  const res = contextArg[TEMP](...args)
  delete contextArg[TEMP]
  return res
}
js
Function.prototype.call_polyfill_1 = function (contextArg, ...args) {
  if (contextArg == null) contextArg = globalThis
  if (typeof contextArg !== 'object') contextArg = new Object(contextArg)
  const TEMP = Symbol()
  contextArg[TEMP] = this
  const res = contextArg[TEMP](...args)
  delete contextArg[TEMP]
  return res
}
js
Function.prototype.bind_polyfill_1 = function (contextArg, ...bindArgs) {
  const self = this
  return function (...args) {
    return self.call_polyfill_1(contextArg, ...bindArgs, ...args)
  }
}

手写 instanceof

前情提要

instanceof 的实现有点指针的味道。

js
function instanceOf(a, b) {
  let p = a
  while (p.__proto__) {
    p = p.__proto__
    if (p === b.prototype) return true
  }
  return false
}

手写 Promise

答案

见这里

实现一个简单的 Ajax

前情提要

首先得熟悉 XMLHttpRequest 的 API, 可查看这个帖子

js
const ajax = (url, method = 'GET', data = null) => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4) {
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
          resolve(JSON.parse(xhr.responseText))
        } else if (xhr.status === 404) {
          reject(new Error('404 Not Found'))
        } else {
          reject(new Error(xhr.status))
        }
      }
    }
    xhr.open(method, url, true)
    xhr.send(data)
  })
}

手写防抖、节流

前情提要

  • 防抖 : 防止抖动, "你先抖动着, 啥时候停了, 再执行下一步"。例如, 一个搜索框, 等输入停止后, 再触发搜索
  • 节流 : 节省交流沟通, "别急, 一个一个来, 按时间节奏来, 插队者无效"。例如, drag 或 scroll 期间触发某个回调, 要设置一个时间间隔
js
// 防抖
function debounce(fn, delay = 500) {
  let timer = null
  return function (...args) {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn.apply(this, args)
      timer = null
    }, delay)
  }
}
// 节流
function throttle(fn, delay = 500) {
  let timer = null
  return function (...args) {
    if (timer) {
      return
    }
    timer = setTimeout(() => {
      fn.apply(this, args)
      timer = null
    }, delay)
  }
}

手写深度比较, 模拟 lodash 的 isEqual

前情提要

主要在于 {a: 1} 与 {a: 1} 要返回 true。

js
const isObject = (target) => typeof target === 'object' && target !== null
const isEqual = (obj1, obj2) => {
  // 只要有一个不是对象, 直接全等判断即可
  if (!isObject(obj1) || !isObject(obj2)) {
    return obj1 === obj2
  }
  // 1. 如果都是对象且引用也一样, 就肯定相等
  if (obj1 === obj2) {
    return true
  }
  // 2. 否则, 判断属性个数是否一样
  const keys1 = Object.keys(obj1)
  const keys2 = Object.keys(obj2)
  if (keys1.length !== keys2.length) {
    return false
  }
  // 3. 属性个数一样就进行递归
  for (key of keys1) {
    const res = isEqual(obj1[key], obj2[key])
    if (!res) {
      return false
    }
  }
  return true
}

手写 flat 函数扁平化数组

前情提要

实现的方法有很多种, 这里讲 concat 方法拍平数组, 如 [].concat([1, 2]) 会自动"消融"一层。

js
// 常规版
function flat(arr) {
  // 1. 验证 arr 中还有没有深层数组
  const isDeep = arr.some((item) => item instanceof Array)
  // 2. 没有直接返回
  if (!isDeep) {
    return arr
  }
  const res = Array.prototype.concat.apply([], arr)
  // 3. 递归
  return flat(res)
}

// 简洁版
function flat(arr) {
  return arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flat(cur) : cur)
  }, [])
}

实现数组去重函数 unique

js
function unique(arr) {
  return [...new Set(arr)]
}

Mochi's personal blog.