JS中箭头函数与this的写法和理解


Posted in Javascript onJanuary 14, 2021

前言

JavaScript在ES6语法中新增了箭头函数,相较于传统函数,箭头函数不仅更加简洁,而且在this方面进行了改进。this作为JavaScript中比较诡异的存在,许多文章对于this的解释也不尽相同,本篇文章试图厘清JS中函数与this的关系。

一、JS中函数的写法

1.常规函数的写法

在ES6语法之前,JS中的函数由function关键字、params参数和被花括号包裹的函数体组成。为了与后面说到的箭头函数相区别,我们先把这样的函数叫做常规函数,常规函数既可以用声明式写法也可以用赋值式写法。例子:

function test(name) { //声明式写法
 console.log(name)
}
test('Jerry')

let test2 = function(name) { //赋值式写法
 console.log(name)
}
test2('Tom')

2. 箭头函数的写法

ES6箭头函数的引入,使函数的写法变的更加简洁,但在书写上要遵循一定的规则。

规则一:箭头函数只能用赋值式写法,不能用声明式写法

例子:

const test = (name) => {
 console.log(name)
}
test('Jerry')

规则二:如果参数只有一个,可以不加括号,如果没有参数或者参数多于一个就需要加括号

例子:

const test = name => {
 console.log(name)
}
test('Jerry')

const test2 = (name1, name2) => {
 console.log(name1 + ' and ' + name2)
}
test2('Tom', 'Jerry')

规则三:如果函数体只有一句话,可以不加花括号

例子:

const test = name => console.log(name)

规则四:如果函数体没有括号,可以不写return,箭头函数会帮你return

例子:

const add = (p1, p2) => p1 + p2
add(10, 25)

记住:函数体的花括号与return关键字同在。

从以上的例子我们可以看出,箭头函数对常规函数的圆括号和花括号都进行了简化。除了这些简化,箭头函数对于常规函数最大的优化之处在于this。

二、理解常规函数中this

在探讨箭头函数对于this的优化之前,我们先得明白this究竟是什么,以及它是如何使用的。this是使用call方法调用函数时传递的第一个参数,它可以在函数调用时修改,在函数没有调用的时候,this的值是无法确定。

如果没有使用过call方法来调用函数的话,上面的对于this的定义可能不太明白。那么我们需要先理解函数调用的两种方法。

1. 纯粹的函数调用

第一种方法最常见,例子如下:

function test(name) {
 console.log(name)
 console.log(this)
}
test('Jerry') //调用函数

这种方法我们使用最多,但是这种函数调用方法只是一种简写,它完整的写法是下面这样的:

function test(name) {
 console.log(name)
 console.log(this)
}
test.call(undefined, 'Tom')

注意到上面调用函数的call方法了吗?call方法接收的第一个参数就是this,这里我们传了一个undefined。那么,依据定义,函数执行了之后打出来的this会是undefined吗?也不是。

如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)。

所以这里我们打出来的this是Window对象。

2. 对象中函数的调用

直接看例子:

const obj = {
 name: 'Jerry',
 greet: function() {
 console.log(this.name)
 }
}
obj.greet() //第一种调用方法
obj.greet.call(obj) //第二种调用方法

例子里第一种调用方法只是第二种调用方法的语法糖,第二种才是完整的调用方法,而且第二种方法厉害的地方在于它可以手动指定this。

手动指定this的例子:

const obj = {
 name: 'Jerry',
 greet: function() {
 console.log(this.name)
 }
}
obj.greet.call({name: 'Spike'}) //打出来的是 Spike

从上面的例子我们看到greet函数执行时this,已经被我们改过了。

3. 构造函数中this

构造函数里的this稍微有点特殊,每个构造函数在new之后都会返回一个对象,这个对象就是this,也就是context上下文。

例子:

function Test() {
 this.name = 'Tom'
}
let p = new Test()
console.log(typeof p) //object
console.log(p.name) // Tom

4. window.setTimeout()和window.setInterval()中函数的调用

window.setTimeout()和window.setInterval()的函数中的this有些特殊,里面的this默认是window对象。

简单总结一下:函数完整的调用方法是使用call方法,包括test.call(context, name)和obj.greet.call(context,name),这里的context就是函数调用时的上下文,也就是this,只不过这个this是可以通过call方法来修改的;构造函数稍微特殊一点,它的this直接指向new之后返回的对象;window.setTimeout()和window.setInterval()默认的是this是window对象。

三、理解箭头函数中的this

上面关于this讲了很多,this是函数用call方法调用时传递的第一个参数,而且它还可以手动更改,这样要确定this的值就太麻烦了。不过,箭头函数的出现给我们确定this帮了一些忙。

1. 箭头函数的特性一:默认绑定外层this

上面提到:this的值是可以用call方法修改的,而且只有在调用的时候我们才能确定this的值。而当我们使用箭头函数的时候,箭头函数会默认帮我们绑定外层this的值,所以在箭头函数中this的值和外层的this是一样的。

不使用箭头函数例子:

const obj = {
	a: function() { console.log(this) } 
}
obj.a() //打出的是obj对象

使用箭头函数的例子:

const obj = {
 a: () => {
 console.log(this)
 }
}
obj.a() //打出来的是window

在使用箭头函数的例子里,因为箭头函数默认不会使用自己的this,而是会和外层的this保持一致,最外层的this就是window对象。

2. 箭头函数的特性二:不能用call方法修改里面的this

这个也很好理解,我们之前一直在说,函数的this可以用call方法来手动指定,而为了减少this的复杂性,箭头函数无法用call方法来指定this。

例子:

const obj = {
 a: () => {
 console.log(this)
 }
}
obj.a.call('123') //打出来的结果依然是window对象

因为上文我们说到window.setTimeout()中函数里的this默认是window,我们也可以通过箭头函数使它的this和外层的this保持一致:

window.setTimeout()的例子:

const obj = {
 a: function() {
 console.log(this)
 window.setTimeout(() => { 
  console.log(this) 
 }, 1000)
 }
}
obj.a.call(obj) //第一个this是obj对象,第二个this还是obj对象

想必大家明白了,函数obj.a没有使用箭头函数,因为它的this还是obj,而setTimeout里的函数使用了箭头函数,所以它会和外层的this保持一致,也是obj;如果setTimeout里的函数没有使用箭头函数,那么它打出来的应该是window对象。

四、多层对象嵌套里函数的this

这里是笔者在学习时遇到的一点疑惑。箭头函数里的this是和外层保持一致的,但是如果这个外层有好多层,那它是和哪层保持一致呢?

直接上例子:

const obj = {
 a: function() { console.log(this) },
 b: {
 	c: function() {console.log(this)}
	}
}
obj.a() // 打出的是obj对象, 相当于obj.a.call(obj)
obj.b.c() //打出的是obj.b对象, 相当于obj.b.c.call(obj.b)

上面的代码都符合直觉,接下来把obj.b.c对应的函数换成箭头函数,结果如下:

const obj = {
 a: function() { console.log(this) },
 b: {
 	c: () => {console.log(this)}
	}
}
obj.a() //没有使用箭头函数打出的是obj
obj.b.c() //打出的是window对象!!

obj.a调用后打出来的是obj对象,而obj.b.c调用后打出的是window对象而非obj,这表示多层对象嵌套里箭头函数里this是和最最外层保持一致的。

上面的内容就是笔者学习箭头函数中梳理出来的知识点,如有错误,请批评指正!这是我在掘金上写的第三篇文章,感谢阅读!

本文参考:this 的值到底是什么?一次说清楚

总结

到此这篇关于JS中箭头函数与this的写法和理解的文章就介绍到这了,更多相关JS箭头函数与this内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
js URL参数的拼接方法比较
Feb 15 Javascript
jQuery淡入淡出元素让其效果更为生动
Sep 01 Javascript
浅谈jquery中delegate()与live()
Jun 22 Javascript
浅谈Javascript中substr和substring的区别
Sep 30 Javascript
Javascript对象字面量的理解
Jun 22 Javascript
Vue.js每天必学之构造器与生命周期
Sep 05 Javascript
JavaScript常用代码书写规范的超全面总结
Sep 11 Javascript
JavaScript学习笔记整理_简单实现枚举类型,扑克牌应用
Sep 19 Javascript
Vue.js常用指令之循环使用v-for指令教程
Jun 27 Javascript
详解webpack分包及异步加载套路
Jun 29 Javascript
如何基于原生javaScript生成带图片的二维码
Nov 21 Javascript
vue切换菜单取消未完成接口请求的案例
Nov 13 Javascript
JavaScript this关键字的深入详解
Jan 14 #Javascript
Vue实现多页签组件
Jan 14 #Vue.js
如何在vue中使用HTML 5 拖放API
Jan 14 #Vue.js
Vue中引入svg图标的两种方式
Jan 14 #Vue.js
vue+element table表格实现动态列筛选的示例代码
Jan 14 #Vue.js
vue 递归组件的简单使用示例
Jan 14 #Vue.js
vue element和nuxt的使用技巧分享
Jan 14 #Vue.js
You might like
php中数字、字符与对象判断函数用法实例
2014/11/26 PHP
php准确获取文件MIME类型的方法
2015/06/17 PHP
解决安装WampServer时提示缺少msvcr110.dll文件的问题
2017/07/09 PHP
javascript iframe内的函数调用实现方法
2009/07/19 Javascript
struts2 jquery 打造无限层次的树
2009/10/23 Javascript
javascript温习的一些笔记 基础常用知识小结
2011/06/22 Javascript
复制js对象方法(详解)
2013/07/08 Javascript
一个js控制的导航菜单实例代码
2013/12/03 Javascript
5个JavaScript经典面试题
2014/10/13 Javascript
javascript操作表格排序实例分析
2015/05/06 Javascript
如何屏蔽防止别的网站嵌入框架代码
2015/08/24 Javascript
浅析JavaScript访问对象属性和方法及区别
2015/11/16 Javascript
JavaScript中的ajax功能的概念和示例详解
2016/10/17 Javascript
Angular使用ng-messages与PHP进行表单数据验证
2016/12/28 Javascript
RequireJS 依赖关系的实例(推荐)
2017/01/21 Javascript
react的滑动图片验证码组件的示例代码
2019/02/27 Javascript
JavaScript仿京东轮播图效果
2021/02/25 Javascript
Python中super()函数简介及用法分享
2016/07/11 Python
Python文本相似性计算之编辑距离详解
2016/11/28 Python
使用Python对Excel进行读写操作
2017/03/30 Python
Python利用BeautifulSoup解析Html的方法示例
2017/07/30 Python
对Python3 pyc 文件的使用详解
2019/02/16 Python
python读出当前时间精度到秒的代码
2019/07/05 Python
在Python中预先初始化列表内容和长度的实现
2019/11/28 Python
TensorFlow 多元函数的极值实例
2020/02/10 Python
python如何实现复制目录到指定目录
2020/02/13 Python
python的help函数如何使用
2020/06/11 Python
html2canvas生成清晰的图片实现打印的示例代码
2019/09/30 HTML / CSS
DataReader和DataSet的异同
2014/12/31 面试题
音乐学个人的自荐书范文
2013/11/26 职场文书
小学英语教学反思
2014/01/30 职场文书
银行批评与自我批评
2014/02/10 职场文书
党员“一帮一”活动总结
2015/05/07 职场文书
同事离别感言
2015/08/04 职场文书
MySQL通过binlog恢复数据
2021/05/27 MySQL
Java 通过手写分布式雪花SnowFlake生成ID方法详解
2022/04/07 Java/Android