JavaScript箭头函数中的this详解


Posted in Javascript onJune 19, 2019

前言

箭头函数极大地简化了this的取值规则。

普通函数与箭头函数

普通函数指的是用function定义的函数:

var hello = function () {
console.log("Hello, Fundebug!");
}

箭头函数指的是用=>定义的函数:

var hello = () => {
console.log("Hello, Fundebug!");
}

JavaScript箭头函数与普通函数不只是写法上的区别,它们还有一些微妙的不同点,其中一个不同点就是this。

箭头函数没有自己的this值,箭头函数中所使用的this来自于函数作用域链。

这句话很简单,不过听着稍微有点莫名其妙,得从头说起。

this到底是什么?

关于this的文章也够多了,有时候越描越黑,我就不再添乱了,我只负责搬运一下MDN文档:this,感兴趣的可以仔细阅读一下,我摘录一些最重要的话就好了。

A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.

JavaScript是一门比较奇特的语言,它的this与其他语言不一样,并且它的取值还取决于代码是否为严格模式("use strict")。

this的值是什么?

The JavaScript context object in which the current code is executing.

this就是代码执行时当前的context object。

Global context

In the global execution context (outside of any function), this refers to the global object whether in strict mode or not.

代码没有在任何函数中执行,而是在全局作用域中执行时,this的值就是global对象,对于浏览器来说,this就是window。

这一条规则还是比较容易接受的。

Function context

Inside a function, the value of this depends on how the function is called.

函数中的this值取决于这个函数是怎样被调用的,这一条规则就有点变态了,也是很容易出BUG的地方。

另外,this的值还与函数是否为严格模式("use strict")有关,这就非常的丧心病狂了...

大家如果好奇的话,出门左转看MDN文档,我多说无益,只说明一种简单的情况。

As an object method

When a function is called as a method of an object, its this is set to the object the method is called on.

当函数作为对象的方法被调用时,它的this值就是该对象。

var circle = {
radius: 10,
getRadius() {
console.log(this.radius);
}
};
circle.getRadius(); // 打印 10

self = this?

当我们需要在对象方法中嵌套一个内层函数时,this就会给我们带来实际的困扰了,大家应该写过这样的代码:

// 使用临时变量self
var circle = {
radius: 10,
outerDiameter() {
var self = this;
var innerDiameter = function() {
console.log(2 * self.radius);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印20

outerDiameter函数是circle对象的方法,因此其this值就是circle对象。

那我们直接写this.radius多好啊,可惜不能这么写,因为内层函数innerDiameter并不会继承外层函数outerDiameter的this值。outerDiameter函数的this值就是circle对象,this.radius等于10。

但是,innerDiameter函数的this值不是circle对象,那它到底是啥?它是innerDiameter函数执行时当前的context object,这个context object又是啥?其实我也晕了,所以不妨测试一下:

// innerDiameter函数中的this是window
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = function() {
console.log(this === window);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印true

innerDiameter函数中的this是window,为啥是window这个不去管它,反正不是circle对象。

因此,如果我们直接在innerDiameter函数中使用this的话,就出问题了:

// 使用普通函数
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = function() {
console.log(2 * this.radius);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印NaN

于是,我们不得不使用一个临时变量self将外层函数outerDiameter的this值搬运到内层函数innerDiameter。

.bind(this)

我们也可以使用.bind(this)来规避this变来变去的问题:

// 使用.bind(this)
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = function() {
console.log(2 * this.radius);
};
innerDiameter = innerDiameter.bind(this);
innerDiameter();
}
};
circle.outerDiameter(); // 打印20

但是,无论是使用临时变量self,还是使用.bind(this),都不是什么很简洁的方式。

总之,普通函数的this取值多少有点奇怪,尤其当我们采用面向对象的方式编程时,很多时候都需要用到this,大多数时候我们都不会去使用.bind(this),而是使用临时变量self或者that来搬运this的取值,写起来当然不是很爽,而且一不小心就会写出BUG来。

正如MDN文档所说:

Until arrow functions, every new function defined its own this value based on how the function was called。This proved to be less than ideal with an object-oriented style of programming.

箭头函数

箭头函数的this取值,规则非常简单,因为this在箭头函数中,可以看做一个普通变量。

An arrow function does not have its own this. The this value of the enclosing lexical scope is used; arrow functions follow the normal variable lookup rules.

箭头函数没有自己的this值,箭头函数中所使用的this都是来自函数作用域链,它的取值遵循普通普通变量一样的规则,在函数作用域链中一层一层往上找。

有了箭头函数,我只要遵守下面的规则,this的问题就可以基本上不用管了:

  • 对于需要使用object.method()方式调用的函数,使用普通函数定义,不要使用箭头函数。对象方法中所使用的this值有确定的含义,指的就是object本身。
  • 其他情况下,全部使用箭头函数。
// 使用箭头函数
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = () => {
console.log(2 * this.radius);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印20

对于内层函数innerDiameter,它本身并没有this值,其使用的this来自作用域链,来自更高层函数的作用域。innerDiameter的外层函数outerDiameter是普通函数,它是有this值的,它的this值就是circle对象。因此,innerDiameter函数中所使用的this来自outerDiameter函数,其值为circle对象。

结论

JavaScript是Brendan Eich花了10天时间设计出来的,因此各种莫名其妙的特性,this也算是其中一个奇葩。好在这些年ECMAScript标准发展很快也很稳定,每年撸一个新的标准,多少可以弥补一下JS的先天不足。

箭头函数对于this取值规则的简化,其实也就是为了少给大家添乱,谁能记得住普通函数this取值的那么多条条框框啊。。。

另外,MDN文档绝对是一个宝藏,大家可以多看看。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
下载网站打开页面后间隔多少时间才显示下载链接地址的代码
Apr 25 Javascript
JavaScript数据存储 Cookie篇
Jul 02 Javascript
js友好的时间返回函数
Aug 24 Javascript
深入理解Node.js的HTTP模块
Oct 12 Javascript
基于jQuery实现左侧菜单栏可折叠功能
Dec 27 Javascript
微信小程序实现换肤功能
Mar 14 Javascript
JavaScript解决浮点数计算不准确问题的方法分析
Jul 09 Javascript
vue 属性拦截实现双向绑定的实例代码
Oct 24 Javascript
详解Bootstrap 学习(一)入门
Apr 12 Javascript
小程序最新获取用户昵称和头像的方法总结
Sep 23 Javascript
JS+CSS实现炫酷光感效果
Sep 05 Javascript
vue 中 get / delete 传递数组参数方法
Mar 23 Vue.js
基于Node.js的大文件分片上传示例
Jun 19 #Javascript
详解在Angular4中使用ng2-baidu-map的方法
Jun 19 #Javascript
了解Javascript中函数作为对象的魅力
Jun 19 #Javascript
利用vue-i18n实现多语言切换效果的方法
Jun 19 #Javascript
使用JQuery自动完成插件Auto Complete详解
Jun 18 #jQuery
使用异步controller与jQuery实现卷帘式分页
Jun 18 #jQuery
使用jQuery mobile NuGet让你的网站在移动设备上同样精彩
Jun 18 #jQuery
You might like
php面向对象 字段的声明与使用
2012/06/14 PHP
php中删除字符串中最先出现某个字符的实现代码
2013/02/03 PHP
ThinkPHP模板范围判断输出In标签与Range标签用法详解
2014/06/30 PHP
PHP远程采集图片详细教程
2014/07/01 PHP
php实现的CSS更新类实例
2014/09/22 PHP
Laravel6.18.19如何优雅的切换发件账户
2020/06/14 PHP
jquery中dom操作和事件的实例学习 下拉框应用
2011/12/01 Javascript
javascript分页代码实例分享(js分页)
2013/12/13 Javascript
Ext4.2的Ext.grid.plugin.RowExpander无法触发事件解决办法
2014/08/15 Javascript
JavaScript中的console.assert()函数介绍
2014/12/29 Javascript
JavaScript中的acos()方法使用详解
2015/06/14 Javascript
JS截取字符串实例详解
2015/11/24 Javascript
KnockoutJS 3.X API 第四章之数据控制流foreach绑定
2016/10/10 Javascript
原生ajax处理json格式数据的实例代码
2016/12/25 Javascript
详解js的六大数据类型
2016/12/27 Javascript
JQuery异步提交表单与文件上传功能示例
2017/01/12 Javascript
AngularJS实现的获取焦点及失去焦点时的表单验证功能示例
2017/10/25 Javascript
ionic3实战教程之随机布局瀑布流的实现方法
2017/12/28 Javascript
vue.js打包之后可能会遇到的坑!
2018/06/03 Javascript
关于自定义Egg.js的请求级别日志详解
2018/12/12 Javascript
利用node 判断打开的是文件 还是 文件夹的实例
2019/06/10 Javascript
JavaScript实现滑动门效果
2020/01/18 Javascript
vue 解决uglifyjs-webpack-plugin打包出现报错的问题
2020/08/04 Javascript
node.js 基于 STMP 协议和 EWS 协议发送邮件
2021/02/14 Javascript
[13:18]《一刀刀一天》之DOTA全时刻21:详解TI新赛制 A队再露獠牙
2014/06/24 DOTA
python中时间、日期、时间戳的转换的实现方法
2019/07/06 Python
Django中celery执行任务结果的保存方法
2019/07/12 Python
卡西欧B级产品官方网站:Casio Outlet
2018/05/22 全球购物
文秘专业自荐信
2013/10/14 职场文书
宝宝周岁宴答谢词
2014/01/26 职场文书
中国梦演讲稿范文
2014/08/28 职场文书
2014年企业党支部工作总结
2014/12/04 职场文书
大一新生军训新闻稿
2015/07/17 职场文书
送给小学生的暑假礼物!小学生必背99首古诗
2019/07/02 职场文书
八年级作文之一起的走过日子
2019/09/17 职场文书
在 Python 中利用 Pool 进行多线程
2022/04/24 Python