(鸡汤文)这一次我终于搞懂了 JavaScript 定时器的 this 指向!
开篇语
忽然有一种感觉,每次学习一个知识点就像是谈一场恋爱:从初次邂逅,到彼此了解,一切都那么的符合恋爱的过程!
如果这个知识点再有点”调皮“的话,那简直是让人欲仙欲死而又不可自拔!因为你永远不知道它还有多少面纱等着你揭开,当你自以为对它已经足够了解的时候,冷不防就是一个盲点迎面砸来。
它简直就像一个”宝藏女孩“,你要时刻做好迎接”惊喜“的准备!
可能正是因为这种新鲜感,我才能一直保持一种类似亢奋的状态吧。当然,这只是针对知识而言,对待情感我还是很保守很专一的<( ̄︶ ̄)>

这两天,我就在和定时器谈恋爱,哦不,是在学习定时器( ̄▽ ̄)~*,可没想到,又给陷进去了……
这不,上一篇文章写完定时器的返回值后,刚觉得自己对它已经了解的清清楚楚明明白白了,够我炫耀一阵子了,谁成想,喘口气的功夫,它又给我整出了幺蛾子。
惑起
写完上篇文章后,我就琢磨着里面的实现代码还可以优化一下,于是给改成了下面这个样子:
<form action="" class="example-form">
<div>
<label for="name">
名称
</label>
<input class="input-ele" type="text" name="name" id="name" placeholder="please input your name"
autocomplete="off">
</div>
<div style="margin-top:50px;">
<label for="res">
输入
</label>
<textarea class="input-ele" type="multipart" name="res" id="res" readonly
placeholder="这里是每一次输入的结果"></textarea>
</div>
</form>
<script>
window.onload = function () {
const resEle = document.querySelector("#res");
function changeOutputVal() {
resEle.value += `\n${ this.value }`;
}
function throttle(fun, delay) {
let last, deferTimer
return function () {
let now = Date.now();
if (last && now < last + delay) {
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fun.apply(this);
}, delay)
} else {
last = now;
fun.apply(this);
}
}
}
const inputEle = document.querySelector("#name");
inputEle.addEventListener("input", throttle(changeOutputVal, 1000));
}
</script>
我的修改依据是:
- throttle 方法返回的是一个匿名函数,这个函数正好充当 input 事件的回调函数
- input 事件回调函数中的 this 指向的是 inputEle
- 匿名函数中将 this 绑定给了 fun 参数,而实际使用中传入的是 changeOutputVal 方法
- 所以 changeOutputVal 方法中的 this 指的就是 inputEle,所以在它里面可以通过
this.value获取到 inputEle 的值
看,这逻辑多严谨,简直头头是道啊 \( ̄︶ ̄)/
按理说,是没问题的吧,结果却出问题了。欲知详情,请看大屏幕:

这个 undefined 是什么鬼?!从哪冒出来的?难道我的延时器没用对?
解惑
面对我的质疑,setTimeout 理直气壮地说:人家回调函数中的 this 本来就是指向 window 对象的嘛,你也没早问啊!
那么,问题来了:为什么延时器中的 this 指向的是 window 呢?setTimeout 自己也解释不清楚了。
得,看来前人诚不我欺也——自己动手,丰衣足食!
凡事不决找 MDN,绝对靠谱!我们来看看 MDN 怎么说:
由
setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。这会导致,这些代码中包含的this关键字在非严格模式会指向window(或全局)对象,严格模式下为 undefined,这和所期望的this的值是不一样的。
看到这个解释,我才明白:this 指向 window 对象,原来是因为执行环境的不同导致的。
在上面的代码中,因为 window 对象没有 value 这个属性,所以 window.value = undefined。
感觉自己在专业的方向上又迈进了一小步,容我小小地嘚瑟一下!

改错
既然知道问题出在哪,那就好办了,我们只需要将 setTimeout 回到函数内部的 this 指向改变一下就好,这里有以下方案。
使用变量引用外部 this
关键代码如下:
window.onload = function () {
// some code here
const that = this;
deferTimer = setTimeout(function () {
last = now;
fun.apply(that);
}, delay)
// some code here
}
使用箭头函数
利用箭头函数不会改变 this 的指向的特性,改造如下:
window.onload = function () {
// some code here
deferTimer = setTimeout(() => {
last = now;
fun.apply(this);
}, delay)
// some code here
}
结束语
知错能改,善莫大焉!
写到这里,我居然体会到了古人那种”朝闻道,夕死可矣“的满足感。
在编程这条路上,可能遍布荆棘,但只要我们勤耕不辍,总能开辟出属于自己的康庄大道!
这鸡汤太美味,我先干为敬,你们随意!b( ̄▽ ̄)d
~
- ~
- 本文完,感谢阅读!
~
学习有趣的知识,结识有趣的朋友,塑造有趣的灵魂!
我是〖编程三昧〗的作者 隐逸王,我的公众号是『编程三昧』,欢迎关注,希望大家多多指教!
你来,怀揣期望,我有墨香相迎! 你归,无论得失,唯以余韵相赠!
知识与技能并重,内力和外功兼修,理论和实践两手都要抓、两手都要硬!
(鸡汤文)这一次我终于搞懂了 JavaScript 定时器的 this 指向!的更多相关文章
- 终于搞懂了vue 的 render 函数(一) -_-|||
终于搞懂了vue 的 render 函数(一) -_-|||:https://blog.csdn.net/sansan_7957/article/details/83014838 render: h ...
- [转]我花了一个五一终于搞懂了OpenLDAP
轻型目录访问协议(英文:Lightweight Directory Access Protocol,缩写:LDAP)是一个开放的,中立的,工业标准的应用协议,通过IP协议提供访问控制和维护分布式信息的 ...
- 探索JAVA并发 - 终于搞懂了sleep/wait/notify/notifyAll
> sleep/wait/notify/notifyAll分别有什么作用?它们的区别是什么?wait时为什么要放在循环里而不能直接用if? ## 简介 首先对几个相关的方法做个简单解释,Obje ...
- 终于搞懂了PR曲线
PR(Precision Recall)曲线 问题 最近项目中遇到一个比较有意思的问题, 如下所示为: 图中的PR曲线很奇怪, 左边从1突然变到0. PR源码分析 为了搞清楚这个问题, 对源码进行了分 ...
- hdu1711(终于搞懂了KMP算法了。。)
题意:给你两个长度分别为n(1 <= N <= 1000000)和m(1 <= M <= 10000)的序列a[]和b[],求b[]序列在a[]序列中出现的首位置.如果没有请输 ...
- Lua的闭包详解(终于搞懂了)
词法定界:当一个函数内嵌套另一个函数的时候,内函数可以访问外部函数的局部变量,这种特征叫做词法定界 table.sort(names,functin (n1,n2) return grades[n1] ...
- 终于搞懂了shell bash cmd...
问题一:DOS与windows中cmd区别 在windows系统中,“开始-运行-cmd”可以打开“cmd.exe”,进行命令行操作. 操作系统可以分成核心(kernel)和Shell(外壳)两部分, ...
- IntelliJ IDEA 部署 Web 项目,终于搞懂了!
这篇牛逼: IDEA 中最重要的各种设置项,就是这个 Project Structre 了,关乎你的项目运行,缺胳膊少腿都不行. 最近公司正好也是用之前自己比较熟悉的IDEA而不是Eclipse,为了 ...
- 终于搞懂Spring中Scope为Request和Session的Bean了
之前只是很模糊的知道其意思,在request scope中,每个request创建一个新的bean,在session scope中,同一session中的bean都是一样的 但是不知道怎么用代码去验证 ...
随机推荐
- maven 中setting.xml
<mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http:/ ...
- 如何在centos上配置802.1Q VLAN标记,linux单网卡多vlan多网段Ip配置案例
介绍 VLAN使将大型网络分成较小且易于管理的网络成为可能.802.1Q是所有供应商都在其网络设备中实施的标准.某些交换机能够将多个VLAN分配给单个网络端口.使用此功能,您可以将多个VLAN分配给单 ...
- Jmeter软件安装之Mac
Jmeter软件安装之Mac 一.环境准备 安装JDK 下载Jmeter 二.下载Jmeter 下载地址: http://jmeter.apache.org/download_jmeter.cgi,下 ...
- flex布局的使用
一.Flex布局是什么? Flex是Flexible Box的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性. 任何一个容器都可以指定为Flex布局. .box{ disp ...
- Hive企业级性能优化
Hive作为大数据平台举足轻重的框架,以其稳定性和简单易用性也成为当前构建企业级数据仓库时使用最多的框架之一. 但是如果我们只局限于会使用Hive,而不考虑性能问题,就难搭建出一个完美的数仓,所以Hi ...
- 腾讯云原生混合云-第三方集群弹EKS应对突发流量的利器
作者 何鹏飞,腾讯云专家产品经理,曾作为容器私有云.TKEStack的产品经理兼架构师,参与腾讯云内部业务.外部客户容器化改造方案设计,目前负责云原生混合云产品方案设计工作. 胡晓亮,腾讯云专家工程师 ...
- 前端Node的实用方法
Node 一.什么是Node Node是以基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动.非阻塞式I/O模型(I/O是 input/output的缩写,即输入输出端口,在 ...
- git远端账号问题
1. gitlab密码修改后git因保存密码无法下载,403 在.git/config添加如下 [credential] helper = store 添加了此项配置之后, github的账号信息就会 ...
- [bug] Openresty:content_by_lua_file 404
参考 http://www.javaear.com/question/28087228.html https://github.com/openresty/openresty.org/issues/1 ...
- GO语言面向对象05---接口的多态
package main import "fmt" type Fighter interface { Attack() (bloodloss int) Defend() } /*骑 ...