「前端每日一问(40)」手写 apply 函数

300 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

本题难度:⭐ ⭐ ⭐

答:

面试过关版

Function.prototype.myApply = function (context = window) {
  context.fn = this
  const args = arguments[1] || []
  const res = context.fn(...args)
  delete context.fn
  return res
}

尽可能完善的版本

Function.prototype.myApply = function (context = window) {
  if (context === undefined || context === null) {
    context = window
  } else {
    context = Object(context)
  }
  const fn = Symbol('fn')
  context[fn] = this
  const args = arguments[1] || []
  const res = context[fn](...args)
  delete context[fn]
  return res
}

卷王版本

Function.prototype.myApply = function (context = window) {
  if (context === undefined || context === null) {
    context = window
  } else {
    context = Object(context)
  }
  const fn = Symbol('fn')
  context[fn] = this
  
  let args = arguments[1]
  if (args) { // 如果有第二个参数,就判断是不是数组或者类数组
    if (!Array.isArray(args) && !isArrayLike(args)) {
      throw new Error('apply函数第二个参数不是数组或类数组')
    }
    args = Array.from(args) // 将类数组转成数组
  } else {
    args = [] // 如果没有第二个参数,就不处理
  }
  
  const res = context[fn](...args)
  
  delete context[fn]
  return res
}

// 判断对象是否是类数组,摘自《JavaScript权威指南》
function isArrayLike(o) {
  if (o &&                                  // o不是null、undefined等
    typeof o === 'object' &&                // o是对象
    isFinite(o.length) &&                   // o.length是有限数值
    o.length >= 0 &&                        // o.length为非负值
    o.length === Math.floor(o.length) &&    // o.length是整数
    o.length < 4294967296)                  // o.length < 2^32
    return true
  else
    return false
}

apply 和 call 的功能是差不多的,唯一的区别是传递函数参数的形式不同,call 是一个参数一个参数地传,apply 是把参数放到一个数组或者类数组集合里,只需要传递这个集合。

他们的实现是完全差不多的,面试过关版尽可能完善版 的实现的细节请参考 call 的实现,就不赘述了。

手写一个 call

至于卷王版本,只是一个戏称,其实一点也不卷,就是多加了类数组的判断,判断一个对象是否是类数组,参考了《JavaScript权威指南》中的 isArrayLike 方法。

关于类数组,可以参考我的这篇文章:

「前端每日一问(18)」JS 中伪数组怎么转成数组

结尾

如果我的文章对你有帮助,你的👍就是对我的最大支持^_^

你也可以关注《前端每日一问》这个专栏,防止失联哦~

我是阿林,输出洞见技术,再会!

上一篇:

「前端每日一问(39)」手写 call 函数

下一篇:

「前端每日一问(41)」手写 bind 函数