高频前端面试考点之手写题(二)

75 阅读3分钟

9. 手写节流函数

节流:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

function throttle(fn, delay) {
  let lastTime = 0
  
  return function (...args) {
    const now = Date.now()
    
    if (now - lastTime >= delay) {
      fn.apply(this, args)
      lastTime = now
    }
  }
}

应用场景:滚动加载、按钮点击。

10. 手写类型判断函数

实现一个全面的类型判断函数,能够识别各种JavaScript数据类型。

function getType(value) {
  if (value === null) return 'null'
  
  const type = typeof value
  if (type !== 'object') return type
  
  return Object.prototype.toString.call(value)
    .replace(/^[object (\S+)]$/, '$1')
    .toLowerCase()
}

注意点:区分 null、数组、普通对象、日期等特殊对象。

11. 手写 call 函数

call 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

Function.prototype.myCall = function(context, ...args) {
  context = context || window
  const fn = Symbol('fn')
  context[fn] = this
  
  const result = context[fn](...args)
  delete context[fn]
  
  return result
}

核心思路:将函数设置为上下文对象的属性,调用后删除。

12. 手写 apply 函数

apply 方法调用一个具有给定 this 值的函数,以及以一个数组(或类数组对象)的形式提供的参数。

Function.prototype.myApply = function(context, args) {
  context = context || window
  const fn = Symbol('fn')
  context[fn] = this
  
  const result = context[fn](...args)
  delete context[fn]
  
  return result
}

与call的区别:参数以数组形式传入。

13. 手写 bind 函数

bind 方法创建一个新的函数,在 bind 被调用时,这个新函数的 this 被指定为 bind 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

Function.prototype.myBind = function(context, ...args1) {
  const self = this
  
  return function(...args2) {
    return self.apply(context, [...args1, ...args2])
  }
}

特点:返回一个新函数,支持参数合并和柯里化。

14. 函数柯里化的实现

柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args)
    } else {
      return function(...args2) {
        return curried.apply(this, args.concat(args2))
      }
    }
  }
}

应用场景:参数复用、延迟执行。

15. 实现AJAX请求

使用原生JavaScript实现AJAX请求。

function ajax(url, method, data, success, fail) {
  const xhr = new XMLHttpRequest()
  xhr.open(method, url, true)
  
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if (xhr.status >= 200 && xhr.status < 300) {
        success(xhr.responseText)
      } else {
        fail(xhr.status)
      }
    }
  }
  
  if (method === 'POST') {
    xhr.setRequestHeader('Content-Type', 'application/json')
    xhr.send(JSON.stringify(data))
  } else {
    xhr.send()
  }
}

16. 使用Promise封装AJAX请求

使用Promise改造AJAX请求,避免回调地狱。

function ajaxPromise(url, method, data) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open(method, url, true)
    
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve(xhr.responseText)
        } else {
          reject(xhr.status)
        }
      }
    }
    
    if (method === 'POST') {
      xhr.setRequestHeader('Content-Type', 'application/json')
      xhr.send(JSON.stringify(data))
    } else {
      xhr.send()
    }
  })
}

17. 实现浅拷贝

浅拷贝只复制一层对象的属性,不复制嵌套对象。

// (1) Object.assign()
const copy = Object.assign({}, original)

// (2) 扩展运算符
const copy = {...original}

// (3) 数组方法实现数组浅拷贝
const copyArr = arr.slice()
const copyArr2 = [].concat(arr)
const copyArr3 = [...arr]

// (4) 手写实现浅拷贝
function shallowCopy(obj) {
  if (typeof obj !== 'object' || obj === null) return obj
  
  const copy = Array.isArray(obj) ? [] : {}
  
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      copy[key] = obj[key]
    }
  }
  
  return copy
}

18. 实现深拷贝

深拷贝会复制对象的所有层级,创建完全独立的副本。

// (1) JSON.stringify()
const copy = JSON.parse(JSON.stringify(original))

// (2) 函数库lodash的_.cloneDeep方法
// const copy = _.cloneDeep(original)

// (3) 手写实现深拷贝函数
function deepCopy(obj, hash = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj
  if (obj instanceof Date) return new Date(obj)
  if (obj instanceof RegExp) return new RegExp(obj)
  
  if (hash.has(obj)) return hash.get(obj)
  
  const copy = Array.isArray(obj) ? [] : {}
  hash.set(obj, copy)
  
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      copy[key] = deepCopy(obj[key], hash)
    }
  }
  
  return copy
}

注意事项:处理循环引用、特殊对象(Date、RegExp等)。