之前咱们说的都是代码内 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 的重构就实现了,是不是很简略呢!