JavaScript内改变this指向

之前我们说的都是代码内 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 的重构就完成了,是不是很简单呢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值