apply
call
bind
这三个函数可以改变 this
的绑定,语法上有略微差别,可以看我很早之前的一篇文章做过简单的介绍 浅谈JavaScript中的apply、call、bind。
this 优先级
-
new
关键字。函数被new
调用,this
指向由new
新构建出来的这个对象 - 函数通过
apply call bind
调用,this
指向绑定的对象 - 函数被调用时,
this
指向调用的对象 - 默认。非严格模式情况下,this指向window, 严格模式下,this指向undefined。
总结一下优先级
new
关键字 > apply call bind
> 函数调用 > 默认
实现
我们既然要重写 apply
,那前两个优先级是用不了的。我们使用第三个优先级——函数调用——改变 this
的指向。
先看下代码
Function.prototype.myApply = function (context, args) {
context = context || window
args = args || []
const key = Symbol() // 保证key的唯一性,避免覆盖原有的属性方法
context[key] = this // 此时的this指向myApply的调用者
const result = context[key](...args)
delete context[key] // 删掉新建的key
return result
}
上面的代码忽略一些边界情况,实现了简易版的 apply
。
看下使用的效果
const person = {
name: 'A',
getName(prefix, suffix) {
return `${prefix} ${this.name} ${suffix}`
}
}
const result = person.getName.myApply({name: 'apply'}, ['one', 'two'])
console.log(result) // one apply two
首先我们创建了一个 Symbol
对象作为 context
的 key
,并把 this
赋值给他。此时的 this
值为 getName
,因为是 getName
调用了 myApply
,所以 this
指向调用者。
当我们以 context[key](...args)
这种方式调用 getName
时,this
指向了 context
,因为是 context
调用了 getName
方法。借此完成了 this
值得转换。
call
和 bind
同理
call
Function.prototype.myCall = function (context, ...args) {
context = context || window
const key = Symbol()
context[key] = this
const result = context[key](...args)
delete context[key]
return result
}
bind
的实现可以使用我们写的 call
函数。需要注意的是 bind
可以传参,新生成的函数也可以传参。
Function.prototype.myBind = function (context, ...args) {
const self = this
return function newFn(...newFnArgs) {
return self.myCall(context, ...args, ...newFnArgs)
}
}