JS中箭头函数与this的写法和理解
前言
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内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!
相关文章
- 本篇文章主要分享了通过window.navigator来判断浏览器及其版本信息的实例代码。具有一定的参考价值,下面跟着小编一起来看下吧...2017-01-23
- 这篇文章主要介绍了js如何实现浏览器打印功能,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-15
- 这篇文章主要给大家介绍了关于Nest.js参数校验和自定义返回数据格式的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-28
- 下面小编就为大家带来一篇利用JS实现点击按钮后图片自动切换的简单方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2016-10-25
- 这篇文章主要介绍了js实现调用网络摄像头及常见错误处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-07
- 这篇文章主要为大家详细介绍了JS实现随机生成验证码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-06
- 作为前端,一直以来都知道HTTP劫持与XSS跨站脚本、CSRF跨站请求伪造。防御这些劫持最好的方法是从后端入手,前端能做的太少。而且由于源码的暴露,攻击者很容易绕过防御手段。但这不代表我们去了解这块的相关知识是没意义的,本文的许多方法,用在其他方面也是大有作用。...2021-05-24
- 这篇文章主要介绍了js组件SlotMachine实现图片切换效果制作抽奖系统的相关资料,需要的朋友可以参考下...2016-04-19
- 这篇文章主要介绍了基于JavaScript实现文字超出部分隐藏 的相关资料,需要的朋友可以参考下...2016-03-01
- 这篇文章主要为大家详细介绍了js实现列表按字母排序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-08-11
- 本文主要介绍this关键字的几种使用方法,this可以代表当前实例,可以调用其他构造函数,还可以用来构建索引器,这里都有一一举例说明。...2020-06-25
- 本文给大家介绍的是nodejs实现使用阿里大鱼短信API发送消息的方法和代码,有需要的小伙伴可以参考下。...2016-01-20
- 这篇文章主要介绍了JS实现响应鼠标点击动画渐变弹出层效果代码,具有非常自然流畅的动画过度效果,涉及JavaScript针对鼠标事件的响应及页面元素样式的动态操作相关技巧,需要的朋友可以参考下...2016-03-28
- Vue.js通过简洁的API提供高效的数据绑定和灵活的组件系统.这篇文章主要介绍了vue.js 表格分页ajax 异步加载数据的相关资料,需要的朋友可以参考下...2016-10-20
- 这次文章要给大家介绍的是node.JS md5加密中文与php结果不一致怎么办,不知道具体解决办法的下面跟小编一起来看看。 因项目需要,需要Node.js与PHP做接口调用,发现nod...2017-07-06
- 系统的学习了一下angularjs,发现angularjs的有些思想根php的模块smarty很像,例如数据绑定,filter。如果对smarty比较熟悉的话,学习angularjs会比较容易一点,这篇文章给大家介绍angularjs filter用法详解,感兴趣的朋友一起学习吧...2015-12-29
- 这篇文章主要为大家详细介绍了js实现上传图片及时预览的相关资料,具有一定的参考价值,感兴趣的朋友可以参考一下...2016-05-09
- 为了网站的安全性,很多朋友都把密码设的比较复杂,但是如何密码不能明显示,不知道输的是对是错,为了安全起见可以把密码显示的,那么基于js代码如何实现的呢?下面通过本文给大家介绍JavaScript实现表单密码的隐藏和显示,需要的朋友参考下...2016-03-03
- v-for标签可以用来遍历数组,将数组的每一个值绑定到相应的视图元素中去,下面这篇文章主要给大家介绍了关于在Vue.js中轻松解决v-for执行出错的三个方案,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。...2017-06-15
- 使用require('crypto')调用加密模块。加密模块需要底层系统提供OpenSSL的支持。它提供了一种安全凭证的封装方式,可以用于HTTPS安全网络以及普通HTTP连接。该模块还提供了一套针对OpenSSL的hash(哈希),hmac(密钥哈希),cipher...2014-06-07