this到底指向哪?一文搞懂this指向

前言

js中的this指向灵活,使用场景多样,概念虽然基础,但却非常重要,非常多隐蔽的bug都来源于它,搞懂this的指向是非常有必要的,如果可以熟练驾驭this,那么代码自然会更为简洁优雅

this指向谁?

关于this指向谁,有一个最通俗的说法
谁调用它,this就指向谁
这样说没什么大的问题,但是并不是特别全面,但是总的来说,他的方向是对的,this的指向是在调用时被确定的,this会指向上下文里调用它的对象,这是最基本的用法

const person = {
	name:'xz',
	sayName:function(){
		console.log(this.name)
	}
}
person.sayName()

这样是没什么问题,也非常好理解,但是如果换个写法

var person = {
	name:'xz',
	sayName:function(){
		console.log(this.name)
	}
}
var name = 'jack'
var sayName = person.sayName
sayName()

这里实际会log出jack,这里就会涉及到一个概念
非显式或隐式调用函数时候,this在严格模式下指向undefined,在非严格模式下指向window
也就是直接调用sayName时,它的this是指向window的,所以他log出了挂在window下的name也就是’jack’

再来看一道升级版的题目,如果搞懂了,this的上下文调用也就没什么问题了

const o1 = {
	text:'o1',
	fn:function(){
		return this.text
	}
}
const o2 = {
	text:'o2',
	fn:function(){
		return o1.fn()
	}
}
const o3 = {
	text:'o3',
	fn:function(){
		var fn = o1.fn
		return fn()
	}
}
console.log(o1.fn())
console.log(o2.fn())
console.log(o3.fn())

答案是o1,o1,undefined
第一个最简单
第二个o2.fn实际上最终执行的还是o1.fn,所以是o1
第三个实际上是裸奔调用,所以指向了window,最终是undefined

call,apply,bind三剑客

call,apply,bind这3个方法都是用来改变this指向的,但是他们具体有什么区别呢
call与apply类似,都是重新绑定this后调用函数,bind则是返回一个绑定好了的函数,需要手动调用
一些基础的用法这里就不再重复说明

const target = {name:'xz'}
const fn = function(arg1,arg2){
    console.log(arg1,arg2)
    console.log(this.name)
}
// 以下3种方式等价,最终都会log出参数1,参数2,xz
fn.call(target,'参数1','参数2')

fn.apply(target,['参数1','参数2'])

fn.bind(target,'参数1','参数2')()

当然,现在面试关于这三剑客的问法已经不再局限于怎么用了,而是如何实现一个,仔细思考其实并不难,fn.bind(obj)实际就是需要让调用方式变成obj.fn,下面是一个简单的bind实现

function myBind (){
    // this是需要绑定的方法
    const me = this
    // 参数转化为数组
    var args = Array.from(arguments)
    // 第一个参数是要bind的上下文
    const ctx = args[0]
    // 其余的是函数的参数
    const fnArgs = args.slice(1,args.length)
    // 返回绑定后的函数
    return ()=>{
        // 使用symbol作为key避免覆盖原属性
        const fn = Symbol('fn')
        // 给需要绑定的上下文添加这个函数作为属性
        ctx[fn] = me
        // 调用函数
        ctx[fn](...fnArgs)
        // 调用后移除该属性
        delete ctx[fn]
    }
}
Function.prototype.myBind = myBind
const obj1 = {name:'xz'}
function fn(age){
    console.log(`Hi,I am ${this.name},I am ${age} years old`)
}
fn.myBind(obj1,18)()
// Hi,I am xz,I am 18 years old

一个建议的bind就完成了,如果是call和apply则不需要return一个function,直接在内部挂载属性调用函数然后删除属性就可以了,三剑客只要掌握一个,另外2个自然也就触类旁通

构造函数的this

说到构造函数,就不得不提new 关键字了,这2者密不可分
new做了什么事,主要有以下4件

  1. 创建一个新对象
  2. 将构造函数的this指向这个新对象
  3. 给这个对象添加属性方法等
  4. 返回这个新的对象

理解了new做了什么,构造函数的this指向哪里自然也就一目了然了,会指向构造函数新构建的对象

function Foo(){
    this.name = 'xz'
}
const me = new Foo()
console.log(me.name) // xz

但是要注意一个特殊情况:构造函数有返回的情况,这种情况又要分为2种情况讨论
返回一个对象或或返回的不是一个对象

function Foo1(){
    this.name='xz'
    const o = {name:'zed'}
    return o
}

function Foo2(){
    this.name = 'xz'
    return 1
}

const me1 = new Foo1()
const me2 = new Foo2()
console.log(me1.name)
// zed
console.log(me2.name)
// xz

可以看到,如果返回的是一个对象,那么this就会指向这个返回的对象
如果返回的不是对象,那么this仍然指向构造的实例

箭头函数的this

箭头函数本身没有this,他的this完全依赖于外层作用域来决定

const foo1 = {
    fn:function(){
        setTimeout(function(){
            console.log(this)
        })
    }
}
const foo2 = {
    fn:function(){
        setTimeout(() => {
            console.log(this)
        })
    }
}
foo1.fn()
// window
foo2.fn()
// foo2

单纯的箭头函数this指向很简单,但如果综合各种情况,并结合this的优先级,就相对没那么容易确定了

this优先级

首先思考如下问题

function foo(){
    console.log(this.a)
}
const obj1 = {
    a:1,
    foo
}
const obj2 = {
    a:2,
    foo
}
obj1.foo.call(obj2)

这个最终会log出2,为什么不是1呢,这就涉及到优先级的问题了
通常我们把根据call,apply,bind,new等关键字进行绑定的情况称为显式绑定,根据调用关系绑定this的方式称为隐式绑定
优先级的先后顺序为隐式绑定 < call,apply,bind < new < 箭头函数this
所以这里通过call绑定的obj2覆盖了隐式绑定的obj1
再来看一个

function Foo(a){
    this.a=a
}
const obj = {}
const bar = Foo.bind(obj)
bar(2)
console.log(obj.a)

const baz = new bar(3)
console.log(baz.a)

这里会log出2和3
首先通过bind将函数的this绑定为obj对象,然后进行赋值,所以obj.a为2
但当他作为构造函数通过new来构建时,new的优先级大于bind,所以他的指向又变成了他构造出的对象

再看最后一个

function foo(){
    return ()=>{
        console.log(this.a)
    }
}
const obj1 = {
    a:2
}
const obj2 = {
    a:3
}
const bar = foo.call(obj1)
bar.apply(obj2)

这里最终会输出2,foo通过call绑定到了obj1上,返回的箭头函数this同时被绑定到obj1
这时再通过apply想改变这个箭头函数的this则是不行的,因为箭头函数的优先级最高,所以this依旧执行obj1,最终log出2

小结

this用法繁多,确实不容易彻底掌握,但是只要能理解他的绑定方式,优先级,则再复杂的场景,也可以抽丝剥茧的找到this具体的指向,this的灵活性使他无法被死记硬背,更多的重在理解

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值