开篇语

忽然有一种感觉,每次学习一个知识点就像是谈一场恋爱:从初次邂逅,到彼此了解,一切都那么的符合恋爱的过程!

如果这个知识点再有点”调皮“的话,那简直是让人欲仙欲死而又不可自拔!因为你永远不知道它还有多少面纱等着你揭开,当你自以为对它已经足够了解的时候,冷不防就是一个盲点迎面砸来。

它简直就像一个”宝藏女孩“,你要时刻做好迎接”惊喜“的准备!

可能正是因为这种新鲜感,我才能一直保持一种类似亢奋的状态吧。当然,这只是针对知识而言,对待情感我还是很保守很专一的<( ̄︶ ̄)>

这两天,我就在和定时器谈恋爱,哦不,是在学习定时器( ̄▽ ̄)~*,可没想到,又给陷进去了……

这不,上一篇文章写完定时器的返回值后,刚觉得自己对它已经了解的清清楚楚明明白白了,够我炫耀一阵子了,谁成想,喘口气的功夫,它又给我整出了幺蛾子。

惑起

写完上篇文章后,我就琢磨着里面的实现代码还可以优化一下,于是给改成了下面这个样子:

<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>

我的修改依据是:

  1. throttle 方法返回的是一个匿名函数,这个函数正好充当 input 事件的回调函数
  2. input 事件回调函数中的 this 指向的是 inputEle
  3. 匿名函数中将 this 绑定给了 fun 参数,而实际使用中传入的是 changeOutputVal 方法
  4. 所以 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 指向!的更多相关文章

  1. 终于搞懂了vue 的 render 函数(一) -_-|||

    终于搞懂了vue 的 render 函数(一) -_-|||:https://blog.csdn.net/sansan_7957/article/details/83014838 render: h ...

  2. [转]我花了一个五一终于搞懂了OpenLDAP

    轻型目录访问协议(英文:Lightweight Directory Access Protocol,缩写:LDAP)是一个开放的,中立的,工业标准的应用协议,通过IP协议提供访问控制和维护分布式信息的 ...

  3. 探索JAVA并发 - 终于搞懂了sleep/wait/notify/notifyAll

    > sleep/wait/notify/notifyAll分别有什么作用?它们的区别是什么?wait时为什么要放在循环里而不能直接用if? ## 简介 首先对几个相关的方法做个简单解释,Obje ...

  4. 终于搞懂了PR曲线

    PR(Precision Recall)曲线 问题 最近项目中遇到一个比较有意思的问题, 如下所示为: 图中的PR曲线很奇怪, 左边从1突然变到0. PR源码分析 为了搞清楚这个问题, 对源码进行了分 ...

  5. hdu1711(终于搞懂了KMP算法了。。)

    题意:给你两个长度分别为n(1 <= N <= 1000000)和m(1 <= M <= 10000)的序列a[]和b[],求b[]序列在a[]序列中出现的首位置.如果没有请输 ...

  6. Lua的闭包详解(终于搞懂了)

    词法定界:当一个函数内嵌套另一个函数的时候,内函数可以访问外部函数的局部变量,这种特征叫做词法定界 table.sort(names,functin (n1,n2) return grades[n1] ...

  7. 终于搞懂了shell bash cmd...

    问题一:DOS与windows中cmd区别 在windows系统中,“开始-运行-cmd”可以打开“cmd.exe”,进行命令行操作. 操作系统可以分成核心(kernel)和Shell(外壳)两部分, ...

  8. IntelliJ IDEA 部署 Web 项目,终于搞懂了!

    这篇牛逼: IDEA 中最重要的各种设置项,就是这个 Project Structre 了,关乎你的项目运行,缺胳膊少腿都不行. 最近公司正好也是用之前自己比较熟悉的IDEA而不是Eclipse,为了 ...

  9. 终于搞懂Spring中Scope为Request和Session的Bean了

    之前只是很模糊的知道其意思,在request scope中,每个request创建一个新的bean,在session scope中,同一session中的bean都是一样的 但是不知道怎么用代码去验证 ...

随机推荐

  1. 【vue-07】vue-router

    Vue-router官网 安装 vue-router是一个插件包,所以我们还是需要用npm 来进行安装.打开命令行工具,进入你的项目目录,输入下面命令. npm install vue-router ...

  2. 【插件篇】前段bootstrap-table-treegrid试手,解决无法显示树形列表或者图标不显示问题。

    说明:具体代码操作我就不贴了.官方有正规的例子!bootstrap-table-examples传送 使用注意事项: 传入的id和pid可以是string类型的(我后台返回的是Long类型转换成str ...

  3. WM_PAINT 与 WM_ERASEBKGND消息的深入分析

    当WM_PAINT消息不是由函数InvalidateRect产生的时(即通过最大话,最小化,移动,下拉菜单等),系统会先产生连续产生若干个WM_ERASEBKGND消息,紧接着在产生WM_PAINT消 ...

  4. 【Mysql】数据库索引,百万数据测试索引效果

    Mysql官方对索引的定义是:索引(index)是帮助Mysql高效获取数据的数据结构.进而,我们可以知道索引的本质是数据结构. 一.索引的分类 主键索引:也就是我们常见的 PRIMARY KEY,只 ...

  5. 使用 Yarn workspace,TypeScript,esbuild,React 和 Express 构建 K8S 云原生应用(一)

    本文将指导您使用 K8S ,Docker,Yarn workspace ,TypeScript,esbuild,Express 和 React 来设置构建一个基本的云原生 Web 应用程序. 在本教程 ...

  6. [bug] python3 pip 安装 MarkupSafe==1.0 失败:ImportError:cannot import name 'Feature' from 'setpools'

    解决 先升级pip到最新版本 python -m pip install --upgrade pip 再升级setuptools pip install --upgrade pip setuptool ...

  7. 【SecureCRT配置】修改默认卷屏行数当做一个操作,屏幕输出有上百行,当需要将屏幕回翻时,这个设置会有很大帮助,默认为500行,可以改为10000行,不用担心找不到了。 选项 => 全局选项 => Default Session => Edit Default Settings => Terminal => Emulation => Scrollback 修改为32000。

    SecureCRT配置屏幕内容输出到log文件 SecureCRT看不到前几分钟操作的内容,或者想把通过vi命令查看的日志输出到log文件(在懒得下载日志文件的情况下),所以接下来就这样操作: 文件保 ...

  8. MergingSort

    递归排序的两种实现 <script type="text/javascript"> //归并排序(递归实现) //思想:堆排序利用了完全二叉树的性质,但是比较麻烦 // ...

  9. 磁盘IO过高时的处理办法 针对系统中磁盘IO负载过高的指导性操作

    磁盘IO过高时的处理办法 针对系统中磁盘IO负载过高的指导性操作 主要命令:echo deadline > /sys/block/sda/queue/scheduler 注:以下的内容仅是提供参 ...

  10. Docker Swarm(三)Service(服务)分配策略

    Service的分配原則 預設分散至多個nodes上 使用率較低的node優先配置 使用者可自行定義此分配模式 Service分配的3種方式 Service Constraints (服务约束) 参考 ...