实现思路:将想要改变的 this
指向的函数放入指向的对象(context
)中
首先考虑一下这样的实现方式:
Function.prototype.myCall = function (context) {
const args = [...arguments].slice(1)
context = context || window
context.fn = this
const res = context.fn(...args)
delete context.fn
return res
}
js
我们来测试一下上面这段代码:
let a = 1
let bar = {
a: 2,
fn() {}
}
function fn() {
console.log(this.a) // 2
}
fn.myCall(bar)
console.log(bar) // {a: 2}
js
我们可以看到函数 fn
的 this
已经指向 obj
,但是 bar
中的 fn
函数被删除了!
这是因为我们将 fn
挂载到 bar
上,但 bar
本来就有 fn
函数,因为被覆盖后被 delete
删除,这污染了 bar
的变量。所以我们可以使用 Symbol
创建一个唯一(unique
)的变量,来确保不会对 bar
对象造成影响。call
代码实现如下:
Function.prototype.myCall = function (context) {
// 获取函数形参
const args = [...arguments].slice(1)
// 防止context上存在同名的fn属性,执行完后会将其删除,污染对象变量
const fn = Symbol()
// 获取指向的对象
context = context || window
// 将想要改变的 this 指向的函数放入指向的对象中
context[fn] = this
// 执行函数,此时 this 指向 context 对象
const res = context[fn](...args)
// 删除对象中的函数
delete context[fn]
return res
}
js
我们再来测试一下:
let a = 1
let bar = {
a: 2,
fn() {}
}
function fn() {
console.log(this.a) // 2
}
fn.myCall(bar)
console.log(bar) // { a: 2, fn: [Function: fn] }
js
可以看到执行 myCall
后,并没有对 bar
中的 fn
的函数影响,this
的指向也已经指向了 bar
。