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

文章目录

  • this指向丢失
  • 多重调用以及箭头函数
  • this指向丢失解决办法
  • bind传递函数参数
  • 总结

this指向丢失

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

function Student(name){this.name=name;this.sayHello=function(){console.log(`Hello, my name is ${this.name}`)}
}
let zhangsan=new Student('zhangsan');
zhangsan.sayHello(); //Hello, my name is zhangsan

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

class People{constructor(name,age){this.name=name;this.age=age;}sayHello(){console.log(`Hello, my name is ${this.name}, I am ${this.age} years old!`);}
}let xiaofu=new People('xiaofu',99);
xiaofu.sayHello(); //Hello, my name is xiaofu, I am 99 years old!

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

但是再看下面这个例子

function Student(name){this.name=name;this.sayHello=function(){console.log(`Hello, my name is ${this.name}`)}
}
let zhangsan=new Student('zhangsan');
// zhangsan.sayHello();
setTimeout(zhangsan.sayHello,2000); //Hello, my name is

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

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

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

let f=zhangsan.sayHello;
setTimeout(f,2000);

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

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

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

function runFunc(func){this.name='lisi';this.sayHello=func;
}
let lisi=new runFunc(zhangsan.sayHello);
lisi.sayHello(); //Hello, my name is lisi

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

多重调用以及箭头函数

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

让我们再看下面这个例子

function Student(name){this.name=name;this.sayHello=function(){console.log(`Hello, my name is ${this.name}`)}this.info={name:'Nobody',sayHello:function(){console.log(this.name)}}
}
let zhangsan=new Student('zhangsan');
zhangsan.info.sayHello(); //Nobody

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

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

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

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

function Student(name){this.name=name;this.sayHello=function(){console.log(`Hello, my name is ${this.name}`)// console.log(this);}this.info={name:'Nobody',// sayHello:function(){console.log(this.name)}sayHello:()=>{console.log(this.name)},test:this.name}
}
let zhangsan=new Student('zhangsan');
zhangsan.info.sayHello(); //zhangsan
console.log(zhangsan.info.test); //zhangsan

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

this指向丢失解决办法

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

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

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

function Student(name){this.name=name;this.sayHello=function(){console.log(`Hello, my name is ${this.name}`)}
}
let zhangsan=new Student('zhangsan');
// zhangsan.sayHello();
setTimeout(function(){zhangsan.sayHello();},2000); //Hello, my name is zhangsan

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

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

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

let boundFunc = func.bind(context);

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

function Student(name){this.name=name;this.sayHello=function(){console.log(`Hello, my name is ${this.name}`)}
}
let zhangsan=new Student('zhangsan');
// zhangsan.sayHello();
setTimeout(zhangsan.sayHello.bind(zhangsan),2000); //Hello, my name is zhangsan

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

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

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

function f() {alert(this.name);
}f = f.bind( {name: "John"} ).bind( {name: "Ann" } );f(); //John

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

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

bind传递函数参数

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

function Student(name){this.name=name;this.sayHello=function(age){console.log(`Hello, my name is ${this.name}, I am ${age} years old.`)}
}
let zhangsan=new Student('zhangsan');
setTimeout(zhangsan.sayHello.bind(zhangsan,99),2000);

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

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

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

function intro(age,name){console.log(`Hello, my name is ${name}, I am ${age} years old`);
}

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

intro99=intro.bind(null,99);
intro99('xiaofu'); //Hello, my name is xiaofu, I am 99 years old

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

总结

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

我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

Javascript中this指向丢失原因及解决办法详解相关推荐

  1. 暗黑3服务器维护能登录,暗黑3登陆错误原因及解决办法详解

    暗黑3登陆错误原因及解决办法详解 2012-10-15 09:06:03来源:游戏下载编辑:评论(0) 暗黑3报错ERROR解决方法,全部暗黑3报错汇总,全部报错汇总解答.小编将玩家遇到的各种暗黑3安 ...

  2. 暗黑破坏神3登录显示无效服务器,暗黑破坏神3——登陆错误原因及解决办法详解...

    暗黑3报错ERROR解决方法,全部暗黑3报错汇总,全部报错汇总解答.小编将玩家遇到的各种暗黑3安装与登陆遇到的错误综合并将全部解决方法告诉大家,希望对玩家有所帮助. 以下是你在安装,游戏,和客户端登陆 ...

  3. 暗黑3登陆游戏显示客户端失去服务器连接,暗黑3登陆错误原因及解决办法详解...

    暗黑3报错ERROR解决方法,全部暗黑3报错汇总,全部报错汇总解答.小编将玩家遇到的各种暗黑3安装与登陆遇到的错误综合并将全部解决方法告诉大家,希望对玩家有所帮助. 以下是你在安装,游戏,和客户端登陆 ...

  4. JavaScript中的onunload不能用的解决办法

    JavaScript中的onunload不能用的解决办法决办法 原文地址:JavaScript中的onunload不能用的解决办法 今天学习JavaScript中的onunload事件,然后自己开始试 ...

  5. HTML游戏黑屏,360安全卫士怎么解决网页游戏黑屏 网页游戏黑屏解决办法详解

    360安全卫士11.6.6.1089 官方正式版 类型:360工具大小:64.0M语言:中文 评分:9.9 标签: 立即下载 网页游戏是现在很多小伙伴们都喜欢玩的,不少小伙伴们在玩的时候最关心的就是怎 ...

  6. 2008系统无法启动服务器不可用,SQL Sever2008r2 数据库服务各种无法启动问题的解决办法(详解)...

    搜索热词 一.sql Server服务远程过程调用失败解决 以前出现过这个问题,那时候是因为把实例安装在了D盘,后来D盘被格式化了.然后,这些就没了.今天早上打开电脑,竟然又出现这个问题,可是Serv ...

  7. Jrebel实现tomcat热部署,遇到的问题以及解决办法,详解

    Jrebel实现tomcat热部署,遇到的问题以及解决办法,详解 参考文章: (1)Jrebel实现tomcat热部署,遇到的问题以及解决办法,详解 (2)https://www.cnblogs.co ...

  8. 进去mysql驱动器没有磁盘_小编教你驱动器中没有磁盘的原因和解决办法

    驱动器中没有软盘?小伙伴你们知道当遇到电脑提示"驱动器中没有软盘"这个问题的讲解办法吗?小编猜应该还是会有部分的小伙伴是不知道该怎么将这个问题给解决好的,所以小编就在下面给你们分享 ...

  9. java中乱码产生的原因_java出现乱码的原因与解决方法详解

    java在字符串中统一用Unicode表示.(推荐:java视频教程) 对于任意一个字符串:String string = "测试字符串"; 如果源文件是GBK编码,操作系统默认环 ...

最新文章

  1. spring boot 启动不连接数据库
  2. Linux中的环境变量知多少?
  3. 【二分图】洛谷P1640连续攻击游戏
  4. 牛顿插值多项式(python实现)
  5. 职业素养和职业技能问题_中职生职业素养提升的综述
  6. matlab储备池算法,储备池计算概述.pdf
  7. jquery实现复选框checkbox全选,取消全选
  8. C语言的getopt
  9. 苹果一半员工未接受4年制大学教育 库克却对此感到骄傲
  10. 电商:购物车模块解决思路
  11. 小米Android系统限制联网,Android 7 以上版本,绕过CA限制,抓取https
  12. android 4.4 webview 上传,android使用webview上傳文件(適配4.4以上系統)
  13. vs 生成com组件 其他电脑_VS也可以这样进行快捷安装
  14. java开发环境有哪些_Java学习开发环境配置大全
  15. 图画日记怎么画_一年级数学图画日记
  16. 启动服务器后台打印系统,服务器打印后台处理
  17. 2021届毕业生还没找到Android开发工作,看这一篇就够了!
  18. 2019天津市计算机等级考试报名时间,天津2020上半年计算机等级考试报名时间已公布...
  19. 如何解决Java查看源代码时页面显示Source not found
  20. Fluent流体材料——NIST Real Gas模型

热门文章

  1. 围棋入门(01)基础知识
  2. 7.设计模式-装饰模式
  3. 解决:苹果手机不能给10开头的号码发短信怎么办?
  4. 南邮和重庆邮电大学计算机专业,高考:南京邮电大学和重庆邮电大学哪个好?...
  5. 软件的设计之生生不息
  6. 记一次自定义Redis分布式锁导致的生产事件时间
  7. 三年磨一剑,高德地图体验优化实践总结
  8. 人才梯队如何搭建,3个维度让你打造一支人才团队
  9. Maya建模中疑难杂症,解决笔记。
  10. 什么是User Story