JS-手写 call

125 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情

前言

手写 call 是一个老生常谈的话题了,但是在日常开发中,除非存在着特殊的需求,不然基本不会用上自己手写的 call,久而久之,也就基本忘却了,真用到时就再也想不起来了。

因此,本文详细记录手写 call 的实现,以备不时之需。

call 是什么

call 是一个函数,主要功能是改变某一个函数的 this 的指向。比如,存在下面两个 Object

const bear_1 = {
  name: 'Bear_1',
  write (writer = 'unkonw', time = 'now') {
    console.log(`My name is ${this.name} --by ${writer} in ${time}`)
  }
}

const bear_2 = {
  name: 'Bear_2'
}

bear_1 有一个 write 方法,但是 bear_2 没有,但是 bear_2 又需要使用这个方法,该如何?

这个时候可以使用 call 方法将 bear_1write 方法的 this 指向 bear_2,也就是使用bear_2 的数据执行 write 方法。

bear_1.say.call(bear_2, 'writeB')
// 输出:My name is Bear_2 --by writeB in now

也可以简单粗暴的看成,bear_1write 方法借给 bear_2 使用,bear_2 执行完 write 方法后就还给 bear_1bear_2 本身依旧不存在 write 方法。

实现

看上去有点复杂,但是实现起来不难。

基本思路:

  1. bear_2 身上创建一个 write 方法;
  2. 执行 bear_2.write
  3. bear_2 上删除 write 方法;
  4. 返回执行结果。

开始写代码,首先在 Function 原型上创建自己的手写 call 函数,函数的参数分为两部分,一部分执行的数据或者作用域 ctx(此处就是 bear_2),可以理解为执行环境的上下文,另一部分参数是arg,执行函数所需的参数:

Function.prototype.myCall = function (ctx, ...arg) {
   // do something
}

注意:不能使用箭头函数。因为箭头函数无法获取本身的 this !

然后,在 bear_2 上创建 write 函数;使用 callwrite 函数就是 call 的调用者 this,指向 write:

Function.prototype.myCall = function (ctx, ...arg) {
    // 创建唯一的 key,避免重复
   const key = Symbol()
   // 挂载函数
   ctx[key] = this
}

注意:key 必须是唯一的,避免覆盖原有的数据。

最后,执行挂载的函数,返回结果:

Function.prototype.myCall = function (ctx, ...arg) {
    // 创建唯一的 key,避免重复
   const key = Symbol()
   // 挂载函数
   ctx[key] = this
   // 获取执行结果
   const res = ctx[key](..arg)
   // 删除函数
   delete ctx[key]
   // 返回执行结果
   return res
}

测试一下:

bear_1.write.myCall(bear_2, 'bear_2', '2012-12-54')
// 输出:My name is Bear_2 --by bear_2 in 2012-12-54