JS中的箭头函数与this
转载自:https://juejin.im/post/5aa1eb056fb9a028b77a66fd#heading-1
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
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对象。
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对象
四、多层对象嵌套里函数的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对象!!
MDN官方文档里面描述箭头函数this定义的时候,描述的是“箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。",所以最后一个例子,打出window对象的原因,是window对象就是它的上一层this,而多层嵌套只是对象嵌套,这时候没有作用域链的嵌套。
JS中的箭头函数与this的更多相关文章
- JS中的普通函数和箭头函数
最近被问到了一个问题: >javaScript 中的箭头函数 (=>) 和普通函数 (function) 有什么区别? 我当时想的就是:这个问题很简单啊~(flag),然后做出了错误的回答 ...
- 简单理解ECMAScript2015中的箭头函数新特性
箭头函数(Arrow functions),是ECMAScript2015中新加的特性,它的产生,主要有以下两个原因:一是使得函数表达式(匿名函数)有更简洁的语法,二是它拥有词法作用域的this值,也 ...
- ES6中的箭头函数与普通函数的区别
箭头函数与普通函数的区别 1.语法上更加简洁.清晰 基本语法: // 关于箭头函数的参数 // 如果箭头函数没有参数,直接写一个括号即可 let fun1 = () => { console.l ...
- js中的回调函数的理解和使用方法
js中的回调函数的理解和使用方法 一. 回调函数的作用 js代码会至上而下一条线执行下去,但是有时候我们需要等到一个操作结束之后再进行下一个操作,这时候就需要用到回调函数. 二. 回调函数的解释 因为 ...
- underscore.js中的节流函数debounce及trottle
函数节流 throttle and debounce的相关总结及想法 一开始函数节流的使用场景是:放止一个按钮多次点击多次触发一个功能函数,所以做了一个clearTimeout setTimeou ...
- ES6中的箭头函数
关于函数表达式中的this:自动引用正在调用当前方法的.前的对象1.obj.fun()中的this fun中的this -> obj2.new Fun() Fun中的this -> 正在创 ...
- js中如何在一个函数里面执行另一个函数
1.js中如何在函数a里面执行函数b function a(参数c){ b(); } function b(参数c){ } 方法2: <script type="text/javasc ...
- JavaScript -- 时光流逝(七):js中的全局函数
JavaScript -- 知识点回顾篇(七):js中的全局函数 全局函数可用于所有内建的 JavaScript 对象. (1) encodeURI():把字符串编码为 URI. <script ...
- js中的匿名函数和匿名自执行函数
1.匿名函数的常见场景 js中的匿名函数是一种很常见的函数类型,比较常见的场景: <input type="button" value="点击" id ...
随机推荐
- CCF模拟题-201909
2.小明种苹果(续)(100分) #include<iostream> #include<cstdio> #include<cstring> #define max ...
- Laravel5 --- QQ邮箱发送邮件
1. 在此之前先确认QQ邮箱是否开启了POP3/SMTP服务,如果未开启则须开启 QQ邮箱->设置->账户->POP3/IMAP/SMTP/Exchange/CardDAV/CalD ...
- python-3-条件判断练习题
前言 我们在前面两章学习了基础数据类型与条件判断语句,今天我们来做下练习题.如果你有不一样的解题思路在评论区亮出你的宝剑!!! 一.习题如下: 1.使用 while 循环输出 1 2 3 4 5 6 ...
- Elasticsearch搜索调优权威指南 (2/3)
本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/AAkVdzmkgdBisuQZldsnvg 英文原文:https://qbox.io/blog/el ...
- jQuery源码分析(九) 异步队列模块 Deferred 详解
deferred对象就是jQuery的回调函数解决方案,它解决了如何处理耗时操作的问题,比如一些Ajax操作,动画操作等.(P.s:紧跟上一节:https://www.cnblogs.com/grea ...
- Autoware 培训笔记 No. 3——录制航迹点
1.前言 航迹点用于知道汽车运行,autoware的每个航迹点包含x, y, z, yaw, velocity信息. 航迹点录制有两种方式,可以开车录制航迹点,也可以采集数据包,线下录制航迹点,我分开 ...
- 大咖云集!Kubernetes and Cloud Native Meetup 深圳站开始报名!
由阿里技术生态联合 CNCF 官方共同出品的 Kubernetes & Cloud Native Meetup 将在 8 月 31 日来到深圳.届时,阿里云.蚂蚁金服高级技术专家将携手来自国内 ...
- Java8新特性——新一套时间API的使用
JDK 1.0中包含了一个java.util.Date类,但是它的大多数方法已经在JDK 1.1引入Calendar类之后被弃用了.而Calendar并不比Date好多少.它们面临的问题是: 可变性: ...
- 使用 Xbox Game 录制桌面视频(录制音频)
使用 Xbox Game 录制桌面视频(附带音频) 前言:可能自己音频输出的问题,一直无法用工具录制桌面的音频,而最后发现利用 Xbox Game 录制游戏视频的功能很好地解决我们的问题. 1)打开游 ...
- 十七:迭代器模式详解(foreach的精髓)
定义:提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示. 从定义中可以看出,迭代器模式是为了在不暴露该对象内部表示的情况下,提供一种顺序访问聚合对象中元素的方法.这种思想在JA ...