Javascript中this指向丢失原因及解决办法详解

大家都知道JS中的this关键字通常出现在函数或者方法中,用来指向调用该函数或者方法的对象。但是在很多时候this的指向却并不总是如我们所愿,这一篇文章就一起来看看到底该如何判断this所指向的对象,同时在this指向丢失情况下如何恢复。

this指向丢失

相信有过面向对象编程经验的朋友对于this的使用不会陌生,来看两个例子

这里的this指向的是构造函数生成的对象zhangsan,对象调用自身的方法sayHello(),其中的this自然不会有什么指向问题。

这里只是把构造函数换成了class语法的方式,this指向的是类实例xiaofu,实例调用自身的方法,其中的this也不会有什么指向问题。

但是再看下面这个例子

注意 setTimeout 的第一个参数是一个函数名,而并不是具体的函数调用。所以这里并不能直接传递zhangsan,sayHello(),不然会马上执行

本意是想等待2秒之后再打印,结果打印完发现this.name并没有打印出来,this指向丢失了。按照this指向调用函数的对象的逻辑,说明2秒后调用sayHello()这个方法的已经不是zhangsan这个对象了。

如果在方法中打印一下this,就会发现此时this指向的是Window。也就是说最后一句可以像如下改写

执行异步操作的时候是将一个函数丢给浏览器,2秒以后,浏览器去直接执行该函数。

这时候可以引出一个重要的结论:包含this的函数无法在定义的时候,而只有在被真正执行的时候才能知道this指向哪个对象。

再看下面的例子就很容易理解了

因为sayHello这个方法真正执行的时候是被lisi这个对象调用,所以this指向的是lisi这个对象,this.name打印了出来也是lisi。

多重调用以及箭头函数

可能有朋友又要问了,那我直接执行zhangsan.sayHello()不也是相当于在Window中去执行这个函数吗?

让我们再看下面这个例子

这里调用的sayHello函数是info对象下的,可以看到函数中的this指向的是info对象,而并不是zhangsan对象。这里又可以引出另外一个重要的结论:多重调用下,函数中的this只会指向函数的上一级对象。这里函数的上一级对象是info,所以虽然zhangsan中也有一个name,但是并不会被引用。

但是这里需要注意的是箭头函数。

箭头函数在ES6中被引入,写起来简洁明了,但是有一个特点需要注意,就是箭头函数没有独立的this,其中的this会自动从上一级继承。

所以如果改写下上面的代码

可以看出,箭头函数中使用this就和直接在info中使用this效果一样,都是指向zhangsan对象。

this指向丢失解决办法

再把话题回到this丢失上面来。

想要恢复this指向,根本逻辑就是想办法还是让this定义时候的对象来调用this所在的函数,回到上面的例子就是让zhangsan来调用sayHello()。

有两种方式可以来实现,第一种是多添加一层函数调用

这里的最后一句相当于Window.zhangsan,sayHello(),根据上面的规则,this指向的是上一级对象,也就是zhangsan,所以可以成功打印出来。

并且这里使用箭头函数同样有效果,因为这里的函数只是起到多加一层包装的作用,并没有实际作用。

第二种方式是利用函数的bind方法,使用语法如下

这里就是将函数func绑定到了context这个上下文上,返回一个新的函数。不管被谁调用,这个新的函数里面的this永远指向context。

这里就是将sayHello()这个方法绑定到了zhangsan这个对象上,以后不管这个返回的新函数被谁调用,都可以成功返回zhangsan中的this.name。

但是这里要注意的是,只能绑定到构造函数返回的具体对象上,而不能直接绑定到类名Student上。

同时要注意bind并不支持级联操作

这里首先将函数f绑定到一个对象,然后马上级联操作绑定到另一个对象,可以看出只有第一个bind起了效果。

同时这里也可以看到只有在函数执行的时候才会将this指向具体的对象

bind传递函数参数

这里再提一个bind方法的进阶用法,就是固定函数传递的一部分参数值,有一点类似python中的partial函数。因为bind方法除了第一个参数是上下文,后面还可以接函数的默认参数值

这里修改了sayHello()方法,必须要传递一个参数,如果想以后每次执行该方法的时候都是传递参数99就可以像上面那样。

下面来一个更通用的例子。

有一个需要传递两个参数的函数如下

通过bind方法将第一个参数值默认为99,并返回一个新函数。这里因为没有context需要传递,所以第一个参数放null,不能省略

注意这里只能是按照参数的先后顺序进行默认值传递,例如这里就不能跨过age给name传递默认值。

总结

JS中的this使用起来并不像其他OOP语言中的类似关键字方便(例如python中的self),因为有指代丢失的问题出现,只能是在实际使用的时候多多练习,熟能生巧了。

参考链接


Javascript中this指向丢失原因及解决办法详解

发布者

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注