Skip to content

手写代码

1. 实现一个 instanceof 函数

js
function _instanceof(obj, fn) {
  let proto = obj.__proto__
  if (proto) {
    return proto === fn.prototype ? true : _instanceof(proto, fn)
  }
  return false
}

2. 实现一个 deepClone 深拷贝

js
function deepClone(target, hash = new WeakMap()) {
  if (target === null) return target
  if (typeof target !== 'object') return target
  if (typeof target === Date) return new Date(target)
  if (typeof target === RegExp) return new RegExp(target)
  if (hash.has(target)) return hash.get(target)

  const cloneTarget = Array.isArray(target) ? [] : {}
  hash.set(target, cloneTarget)
  for (let key in target) {
    if (target.hasOwnProperty(key)) {
      cloneTarget[key] = deepClone(target[key], hash)
    }
  }
  return cloneTarget
}

3. 实现一个 new 操作符

js
function _new(fn, ...args) {
  let obj = Object.create(fn.prototype)
  let result = fn.apply(obj, args)
  return result instanceof Object ? result : obj
}

4. 实现一个 Object.create()

js
function create(proto, propertiesObject) {
  if (typeof proto !== 'object' && typeof proto !== 'function') {
    throw new TypeError('Object prototype may only be an Object or null')
  }
  if (propertiesObject === null) {
    throw new TypeError('Cannot convert undefined or null to object')
  }

  function F() {}

  F.prototype = proto
  const obj = new F()

  if (propertiesObject !== undefined) {
    Object.defineProperties(obj, propertiesObject)
  }
  if (proto === null) {
    obj.__proto = null
  }

  return obj
}

5. 实现一个 call 函数

js
Function.prototype._call = function (context, ...args) {
  // 类型检查
  if (typeof this !== 'function') {
    throw new TypeError('调用对象必须为函数')
  }

  // 处理context默认值
  const ctx = context || window
  // 创建唯一属性避免污染
  const _fn = Symbol('fn')
  ctx[_fn] = this
  // 执行函数并处理参数
  let result = ctx[_fn](...args)
  // 删除临时属性
  delete ctx[_fn]
  return result
}

6. 实现一个 apply 函数

js
Function.prototype._apply = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('调用对象必须为函数')
  }

  const ctx = context || window
  const _fn = Symbol('fn')
  ctx[_fn] = this
  let result = arguments[1] ? ctx[_fn](arguments[1]) : ctx[_fn]()
  delete ctx[_fn]
  return result
}

7. 实现一个 bind 函数

js
Function.prototype._bind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('调用对象必须为函数')
  }

  const ctx = context || window
  let fn = this
  let args = Array.prototype.slice.call(arguments, 1)
  const bound = function () {
    const allArgs = args.concat(Array.prototype.slice.call(arguments, 1))
    // 处理new调用情况
    if (this instanceof bound) {
      return new fn(...allArgs)
    }
    // 处理普通调用
    return fn.apply(ctx, allArgs)
  }
  // 维护原型链
  if (fn.prototype) {
    bound.prototype = Object.create(fn.prototype)
  }
  return bound
}

8. 实现一个 debounce 防抖函数

js
/**
 * 防抖函数
 * @param {Function} fn 目标函数
 * @param {number} wait 延迟时间(毫秒)
 * @param {boolean} immediate 是否立即执行
 * @return {Function} 返回防抖处理后的函数
 */

function debounce(fn, wait = 300, immediate = false) {
  let timer = null
  let result
  const debounced = function (...args) {
    const that = this
    // 清除已有定时器
    if (timer) clearTimeout(timer)
    // 立即执行模式
    if (immediate) {
      const callNow = !timer
      timer = setTimeout(() => {
        timer = null
      }, wait)
      if (callNow) result = fn.apply(that, args)
    }
    // 延迟执行模式
    else {
      timer = setTimeout(() => {
        fn.apply(that, args)
      }, wait)
    }
    return result
  }
}

// 添加取消功能
debounce.cancel = function (debounceFn) {
  if (debounceFn && debounceFn.timer) {
    clearTimeout(debounceFn.timer)
    debounceFn.timer = null
  }
}

9. 实现一个 throttler 节流函数

js
function throttler(fn, wait = 300) {
  let timer = null
  return function (...args) {
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, args)
        timer = null
      }, wait)
    }
  }
}

10. 实现数组的原生方法 MapFilterReduce

Map
js
Array.prototype._map = function (callback) {
  const result = []
  for (let i = 0; i < this.length; i++) {
    result.push(callback(this[i], i, this))
  }
  return result
}

// 使用示例
const numbers = [1, 2, 3]
const doubled = numbers._map((num) => num * 2)
console.log(doubled) // 输出: [2, 4, 6]
Filter
js
Array.prototype._filter = function (callback) {
  const result = []
  for (let i = 0; i < this.length; i++) {
    if (callback(this[i], i, this)) {
      result.push(this[i])
    }
  }
  return result
}

// 使用示例
const numbers = [1, 2, 3, 4, 5]
const evens = numbers._filter((num) => num % 2 === 0)
console.log(evens) // 输出: [2, 4]
Reduce
js
Array.prototype._reduce = function (callback, initiaValue) {
  let accumulator = initiaValue !== undefined ? initiaValue : this[0]
  let startIndex = initiaValue !== undefined ? 0 : 1

  for (let i = startIndex; i < this.length; i++) {
    accumulator = callback(accumulator, this[i], i, this)
  }

  return accumulator
}

// 使用示例
const numbers = [1, 2, 3, 4]
const sum = numbers._reduce((acc, num) => acc + num, 0)
console.log(sum) // 输出: 10

11. 实现一个 uniqueArray 数组去重

js
// 方法1:
function uniqueArray(arr) {
  return [...new Set(arr)]
}

// 方法2:
function uniqueArray(arr) {
  return arr.filter((item, index) => {
    return arr.indexOf(item) === index
  })
}

// 方法3:
function uniqueArray(arr) {
  return arr.reduce((pre, cur) => {
    return pre.include(cur) ? pre : [...pre, cur]
  }, [])
}

12. 实现一个 flat 数组扁平化

js
Array.prototype._flat = function (deep = 1) {
  let arr = this
  if (deep < 1) return
  return arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? cur._flat(deep - 1) : cur)
  }, [])
}

13. 实现一个 faltObj 对象扁平化

js
function falttenObj(obj, parentKey = '', result = {}) {
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      let newKey = parentKey ? `${parentKey}.${key}` : key

      if (typeof obj[key] === 'object' && obj[key] !== null) {
        falttenObj(obj[key], newKey, result)
      } else {
        result[key] = obj[key]
      }
    }
  }
  return result
}

14. 实现一个可迭代的对象

js
const CountIoN = {
  [Symbol.iterator]() {
    let current = 1
    const max = 5
    return {
      next() {
        if (current <= max) {
          return { value: current++, done: false }
        }
        return { value: undefined, done: true }
      }
    }
  }
}

// 测试
for (const num of CountIoN) {
  console.log(num) // 输出:1,2,3,4,5
}

15. 实现继承

寄生组合继承
js
function Parent(name) {
  this.name = name
  return this
}

Parent.prototype.getName = function () {
  return this.name
}
Parent.prototype.info = {
  male: 'man'
}

function Children(age, name) {
  Parent.call(this, name)
  this.age = age
}

Children.prototype = Object.create(Parent.prototype)
Children.prototype.constructor = Children

// 示例
const child1 = new Children(1, 'Tom')
const child2 = new Children(2, 'Jact')

console.log(child1.name)
console.log(child2.name)
console.log(child2.info)
console.log(child2.getName())
ES6 Class类继承
js
class Parent {
  constructor(name) {
    this.name = name
  }

  getName() {
    return this.name
  }
}

class Children extends Parent {
  constructor(age, name) {
    super(name)
    this.age = age
  }

  getInfo() {
    return `${this.name}-${this.age}`
  }
}

// 示例
const child = new Children(12, 'Jack')
console.log(child.getName())
console.log(child.getInfo())

16. 实现 sleep 函数

js
function sleep(delay) {
  return function () {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, delay)
    })
  }
}

// 测试
let promise = new Promise(function (rs, rj) {
  console.log('do something')
  rs()
})
  .then(sleep(2000))
  .then(function () {
    console.log('after sleep 2000')
  })

17. 实现带 取消功能的sleep 函数

实现原理:利用Promise.race方法不管哪个promise先执行完promise都fulfilled的特点,中途调用sleep.cancel() 方法提前完成另外一个promise,从而使原本需要异步执行的promise失效

js
function sleep(delay) {
  let _cancel = null
  const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('这是正常的结果')
    }, delay)
  })
  const p2 = new Promise((resolve, reject) => {
    // 这里是cancel方法
    _cancel = function () {
      resolve('这是取消的resolve结果')
    }
  })
  const race = Promise.race([p1, p2])
  // 返回结果上绑定一个cancel方法
  race.cancel = _cancel
  return race
}

// 测试
const p = sleep(5000)
p.then((res) => {
  console.log(res)
})
p.cancel()

18. 函数柯里化

js
const currying = (fn, ...args) => {
  return (...subArgs) => {
    const allArgs = [...args, ...subArgs]
    if (allArgs.length < fn.length) {
      return currying(fn, ...allArgs)
    } else {
      return fn(...allArgs)
    }
  }
}

// 测试
const sum = (a, b, c, d) => a + b + c + d
console.log(currying(sum, 1)(2)(3, 4))

19. 实现一个 compose 组合函数

js
const _compose = (...fns) => {
  return (...args) => {
    return fns.reduceRight(
      (pre, cur) => {
        return cur(pre)
      },
      ...args
    )
  }
}

// 测试
const add = (x) => x + 1
const double = (x) => x * 2
console.log(_compose(double, add)(5))

20. 惰性函数

惰性函数就是解决每次都要进行判断的这个问题

js
// 简化写法
function addEvent(type, el, fn) {
  // 问题在于每当使用一次 addEvent 时都会进行一次判断。
  if (window.addEventListener) {
    el.addEventListener(type, fn, false)
  } else if (window.attachEvent) {
    el.attachEvent('on' + type, fn)
  }
}

// 利用惰性函数
function addEvent(type, el, fn) {
  if (window.addEventListener) {
    addEvent = function (type, el, fn) {
      el.addEventListener(type, fn, false)
    }
  } else if (window.attachEvent) {
    addEvent = function (type, el, fn) {
      el.attachEvent('on' + type, fn)
    }
  }
}

// 也可以使用闭包
var addEvent = (function () {
  if (window.addEventListener) {
    return function (type, el, fn) {
      el.addEventListener(type, fn, false)
    }
  } else if (window.attachEvent) {
    return function (type, el, fn) {
      el.attachEvent('on' + type, fn)
    }
  }
})()

21. 实现 JSONP

js
// 生成随机回调函数名
function generateCallbackName() {
  return 'jsonp_' + Math.random().toString(36).substr(2)
}

// JSONP请求函数
function jsonp(url, callback) {
  const callbackName = generateCallbackName()
  // 创建全局回调函数
  window[callbackName] = function (data) {
    callback(data)
    delete window[callbackName]
    document.body.removeChild(script)
  }

  // 创建script标签
  const script = document.createElement('script')
  script.src = `${url}?callback=${callbackName}`
  document.body.appendChild(script)
}

// 使用示例
jsonp('http://localhost:8000/api', function (data) {
  console.log('收到数据:', data)
})

22. 图片懒加载

js
// <img src="./load.png" data-src="" />
function _observerImg() {
  // 获取所有的图片元素
  let imgList = document.getElementsByTagName('img')
  let observer = new IntersectionObserver((list) => {
    // 回调是数组
    list.forEach((item) => {
      // 判断元素是否出现在视口
      if (item.intersectionRatio > 0) {
        item.target.src = item.target.getAttribute('data-src')
        // 设置src属性后,停止监听
        observer.unobserve(item.target)
      }
    })
  })

  // 监听每个img元素
  for (let i = 0; i < imgList.length; i++) {
    observer.observer(imgList[i])
  }
}

23. 判断元素是否达到可视区域

方法一
js
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    console.log(entry)
    if (entry.isIntersecting) {
      console.log('在可视区域')
    } else {
      console.log('不在')
    }
  })
})

// 示例
const targetElement = document.querySelector('#myElement')
observer.observe(targetElement)
方法二
js
function isInViewport(element) {
  const rect = element.getBoundingClientRect()
  return (
    rect.top >= 0 &&
    rect.left >= 0 &&
    rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
    rect.right <= (window.innerWidth || document.documentElement.clientWidth)
  )
}
方法三
js
function isInViewport(element) {
  const clientHeight = window.innerHeight || document.documentElement.clientHeight
  const offsetTop = element.offsetTop
  const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
  console.log(offsetTop, scrollTop, clientHeight)
  if (offsetTop - scrollTop <= clientHeight) {
    console.log('进入可视区域')
  }
}

// 示例
const targetElement = document.querySelector('.name04')

let ticking = false
window.addEventListener('scroll', () => {
  if (!ticking) {
    window.requestAnimationFrame(() => {
      isInViewport(targetElement)
      ticking = false
    })
    ticking = true
  }
})

24. 封装一个 Ajax 请求

js
function ajax(options) {
  const xhr = new XMLHttpRequest()

  options.type = (options.type || 'GET').toUpperCase()
  options.dataType = options.dataType || 'json'
  options.timeout = options.timeout || 10000

  const params = formatParams(options.data)
  let timer

  // 格式化参数
  function formatParams(data) {
    let arr = []
    for (let name in data) {
      arr.push(`${encodeURIComponent(name)}=${encodeURIComponent(data[name])}`)
    }
    return arr.join('&')
  }

  // 返回 Promise 对象

  return new Promise((resolve, reject) => {
    // GET请求处理
    if (options.type === 'GET') {
      xhr.open('GET', options.url + '?' + params, true)
      xhr.send(null)
    }
    // POST请求处理
    else if (options.type === 'POST') {
      xhr.open('POST', options.url)
      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') // 设置请求头
      xhr.send(params)
    }
    // 超时处理
    timer = setTimeout(() => {
      xhr.abort()
      reject(new Error('请求超时'))
    }, options.timeout)

    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        clearTimeout(timer)
        const status = xhr.status
        if (status >= 200 && status < 300) {
          let response = xhr.responseText
          if (options.dataType === 'json') {
            try {
              response = JSON.parse(response)
            } catch (e) {
              reject(new Error('JSON解析失败'))
            }
          }
          resolve(response)
        }
      } else {
        reject(new Error(`请求失败:${status}`))
      }
    }
  })
}

25. 实现 EventEmitter 发布订阅

js
class EventEmitter {
  constructor() {
    this.events = {}
  }

  // 订阅事件
  on(eventName, cb) {
    if (!this.events[eventName]) {
      this.events[eventName] = []
    }
    this.events[eventName].push(cb)
  }

  // 取消订阅
  off(eventName, cb) {
    if (!this.events[eventName]) return
    if (!cb) {
      this.events[eventName] = null
    }
    this.events[eventName].filter((fn) => fn !== cb)
  }

  // 发布事件
  emit(eventName, ...args) {
    if (!this.events[eventName]) {
      throw new Error(`${eventName} event is not registered`)
    }
    this.events[eventName].forEach((fn) => fn(...args))
  }

  // 一次性订阅
  once(eventName, cb) {
    const func = (...args) => {
      cb.apply(this, args)
      this.off(eventName, func)
    }
    this.on(eventName, func)
  }
}

26. 实现一个深度比较 deepEqual

js
function deepEqual(a, b) {
  // 处理基本类型和null / undefined
  if (a === b) return true
  if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') {
    return a === b
  }
  // 处理Date类型
  if (a instanceof Date && b instanceof Date) {
    return a.getTime() === b.getTime()
  }
  // 处理RegExp类型
  if (a instanceof RegExp && b instanceof RegExp) {
    return a.toString() === b.toString()
  }
  // 处理数组
  if (Array.isArray(a) && Array.isArray(b)) {
    if (a.length !== b.length) return false
    for (let i = 0; i < a.length; i++) {
      if (deepEqual(a[i], b[i])) return false
    }
    return true
  }
  // 处理对象
  const keysA = Object.keys(a)
  const keysB = Object.keys(b)
  if (keysA.length !== keysB.length) return false
  for (const key of keysA) {
    if (!keysB.includes(key) || !deepEqual(a[key], b[key])) return false
  }
  return true
}

基于 MIT 许可发布