Skip to content
On this page

call 实现

实现思路:将想要改变的 this 指向的函数放入指向的对象(context)中

首先考虑一下这样的实现方式:

js
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}

我们可以看到函数 fnthis 已经指向 obj,但是 bar 中的 fn 函数被删除了!

这是因为我们将 fn 挂载到 bar 上,但 bar 本来就有 fn 函数,因为被覆盖后被 delete 删除,这污染了 bar 的变量。所以我们可以使用 Symbol 创建一个唯一(unique)的变量,来确保不会对 bar 对象造成影响。call 代码实现如下:

js
Function.prototype.myCall = function (context) {
  const args = [...arguments].slice(1) // 获取函数形参
  const fn = Symbol() // 防止context上存在同名的fn属性,执行完后会将其删除,污染对象变量
  context = context || window // 获取指向的对象
  context[fn] = this // 将想要改变的 this 指向的函数放入指向的对象中
  const res = context[fn](...args) // 执行函数,此时 this 指向 context 对象
  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] }

可以看到执行 myCall后,并没有对 bar 中的 fn 的函数影响,this 的指向也已经指向了 bar

Released under the MIT License.