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