之前咱们说的都是代码内 this 的默认指向
明天咱们要来说一下如何能扭转 this 指向
也就是说, 你指向哪我不论, 我让你指向哪, 你就得指向哪
开局在函数的原型 (Function.prototype) 上有三个办法
call
apply
bind
既然是在函数的原型上, 那么只有是函数就能够调用这三个办法,他们三个的作用就是扭转函数的 this 指向接下来咱们便开始介绍这三个办法
筹备一个函数
const obj = {name: '对象'}
const arr = [100, 200, 300]
function fn(a, b) {console.group('fn 函数内的打印信息')
console.log('a :', a)
console.log('b :', b)
console.log('this :', this)
console.groupEnd()}
fn(1, 2)
间接调用函数, 依照 this 指向规定, 该函数内的 this 会指向 window
1 是传递给形参 a 的数据
2 是传递给形参 b 的数据
call 办法
语法: 函数.call(函数内的 this 指向, 给函数传递的参数, 给函数传递的参数)
作用: 扭转函数内的 this 指向
fn.call(obj, 1, 2)
利用 call 调用 fn 函数, 此时 obj 就是 fn 函数内的 this 指向
1 是传递给形参 a 的数据
2 是传递给形参 b 的数据
apply 办法
语法: 函数.apply(函数内的 this 指向, 数组类数据结构)
作用: 扭转函数内的 this 指向
其实 apply 实质上和 call 办法没有区别, 只是给函数传递参数的形式不一样 ,apply 的第二个参数是一个数组类的数据结构, 只有是依照索引排列即可
该数据结构内的每一个顺次是给函数进行形参赋值的数据
fn.apply(arr, [ 10, 20])
利用 apply 调用 fn 函数, 此时 arr 就是 fn 函数内的 this 指向, 第二个参数是一个数组
该数组内 [0] 数据是传递给形参 a 的数据
该数组内 [1] 数据是传递给形参 b 的数据
bind 办法
语法: 函数.bind(函数内的 this 指向, 给函数传递的参数, 给函数传递的参数)
作用: 扭转函数内的 this 指向
其实 bind 实质上和 call 办法没有区别, 然而 bind 不会立刻调用函数, 而是返回一个被锁定好 this 的新函数
const res = fn.bind(obj, 100, 200)
利用 bind 调用 fn 函数, 此时 obj 就是你想设置的 fn 函数内的 this 指向
100 是传递给形参 a 的数据
200 是传递给形参 b 的数据
然而, 其实并不会立刻执行 fn 函数, 而是依据 fn 函数复刻了一份截然不同的函数, 新函数复制给了 res 变量,res 函数内的 this 被锁定为了 obj.
通过浏览器查看咱们会发现 fn 函数并没有被调用, 那是因为 bind 自身就不会调用 fn 函数, 如果想指向, 须要通过 res 变量调用
res()
重构
下面咱们介绍了一下三个办法的用法, 如果你能把握好, 那么接下来就来看看这三个办法是如何实现的吧
call 办法重构
// thisArg 拿到的是你要扭转的 this 指向
// args 拿到剩下所有的内容, 是负责传递给指标函数的
Function.prototype.myCall = function (thisArg, ...args) {
thisArg = thisArg || windon // 如果没有这个参数, 就设置为 window
const fnKey = Symbol('fn') // 创立一个惟一 key 作为函数的标识
thisArg[fnKey] = this // 利用惟一标识把以后函数增加到指定对象中
const res = thisArg[fnKey](...args) // 利用对象和惟一标识来调用函数, 这样就相当于对象在调用函数, this 指向天然被扭转了, 在这里不要忘了把给函数筹备的参数传递进去, 并且承受一下返回值
delete thisArg[fnKey] // 用完当前就删除掉这个长期对象成员
return res // 把承受到的函数返回值返进来就能够了
}
这样, 咱们的 call 重构就实现了
apply 办法重构
这个其实和 call 办法是差不多的, 只是参数不一样了而已, 只是依据调用形式的不同, 咱们承受参数不在须要 …args, 间接接管即可
// thisArg 拿到的是你要扭转的 this 指向
// args 拿到剩下所有的内容, 是负责传递给指标函数的
Function.prototype.myApply = function (thisArg, args) {
thisArg = thisArg || windon // 如果没有这个参数, 就设置为 window
const fnKey = Symbol('fn') // 创立一个惟一 key 作为函数的标识
thisArg[fnKey] = this // 利用惟一标识把以后函数增加到指定对象中
const res = thisArg[fnKey](...args) // 利用对象和惟一标识来调用函数, 这样就相当于对象在调用函数, this 指向天然被扭转了, 在这里不要忘了把给函数筹备的参数传递进去, 并且承受一下返回值
delete thisArg[fnKey] // 用完当前就删除掉这个长期对象成员
return res // 把承受到的函数返回值返进来就能够了
}
bind 办法重构
这个办法其实也是非常简单, 只有利用一下之前重构好的 call 或者 apply 办法都能够
// thisArg 拿到的是你要扭转的 this 指向
// args 拿到剩下所有的内容, 是负责传递给指标函数的
Function.prototype.myApply = function (thisArg, ...args) {
const fn = this // 利用一个变量把指标函数保留下来
// 返回一个新的函数即可
return function (...innerArgs) {
// 利用 myApply 办法调用指标函数, 并且优先初始传递的参数
return fn.myApply(fn, [ ...args, ...innerArgs])
}
}
此时 bind 的重构就实现了, 是不是很简略呢!