持续创作,加速成长!这是我参与「掘金日新计划 · 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_1 的 write 方法的 this 指向 bear_2,也就是使用bear_2 的数据执行 write 方法。
bear_1.say.call(bear_2, 'writeB')
// 输出:My name is Bear_2 --by writeB in now
也可以简单粗暴的看成,bear_1 把 write 方法借给 bear_2 使用,bear_2 执行完 write 方法后就还给 bear_1,bear_2 本身依旧不存在 write 方法。
实现
看上去有点复杂,但是实现起来不难。
基本思路:
- 在
bear_2身上创建一个write方法;- 执行
bear_2.write;- 从
bear_2上删除write方法;- 返回执行结果。
开始写代码,首先在 Function 原型上创建自己的手写 call 函数,函数的参数分为两部分,一部分执行的数据或者作用域 ctx(此处就是 bear_2),可以理解为执行环境的上下文,另一部分参数是arg,执行函数所需的参数:
Function.prototype.myCall = function (ctx, ...arg) {
// do something
}
注意:不能使用箭头函数。因为箭头函数无法获取本身的
this!
然后,在 bear_2 上创建 write 函数;使用 call,write 函数就是 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