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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入理解Javascript箭头函数中的this

    首先我们先看一段代码,这是一个实现倒数功能的类「Countdown」及其实例化的过程: function Countdown(seconds) { this._seconds = seconds; } Countdown.prototype._step = function() { console.log(this._seconds); if (this._seconds > 0) { this._seconds -= 1; } else { clearInterval(this._timer)

  • JavaScript基础之this和箭头函数详析

    箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this. 由于箭头函数没有自己的this指针,通过 call() 或 apply() 方法调用一个函数时,只能传递参数(不能绑定this),他们的第一个参数会被忽略. 前言 阅读下面的内容要先看下<你不知道的javascript(上)>中的第二部分:this和对象原型(里面对于this指向的理论部分主要来源于该书). 问题 这周在写代码的时候,听同事在说箭头函数无法更改this的指向,并且使用下面代码进行验证: // (1)

  • JavaScript箭头函数中的this详解

    前言 箭头函数极大地简化了this的取值规则. 普通函数与箭头函数 普通函数指的是用function定义的函数: var hello = function () { console.log("Hello, Fundebug!"); } 箭头函数指的是用=>定义的函数: var hello = () => { console.log("Hello, Fundebug!"); } JavaScript箭头函数与普通函数不只是写法上的区别,它们还有一些微妙的不

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

    前言 JavaScript在ES6语法中新增了箭头函数,相较于传统函数,箭头函数不仅更加简洁,而且在this方面进行了改进.this作为JavaScript中比较诡异的存在,许多文章对于this的解释也不尽相同,本篇文章试图厘清JS中函数与this的关系. 一.JS中函数的写法 1.常规函数的写法 在ES6语法之前,JS中的函数由function关键字.params参数和被花括号包裹的函数体组成.为了与后面说到的箭头函数相区别,我们先把这样的函数叫做常规函数,常规函数既可以用声明式写法也可以用赋

  • js中匿名函数的N种写法

    匿名函数没有实际名字,也没有指针,怎么执行滴? 其实大家可以看看小括号的意义就应该可以理解.小括号有返回值,也就是小括号内的函数或者表达式的返回值,所以说小括号内的function返回值等于小括号的返回值,不难理解 (function(){})()可以将没有名字的函数执行了把- 关于匿名函数写法,很发散~ 最常见的用法: 复制代码 代码如下: (function() { alert('water'); })(); 当然也可以带参数: 复制代码 代码如下: (function(o) { alert

  • Angular.js中window.onload(),$(document).ready()的写法浅析

    一,问题发现: 最近公司有个微信公众号项目,为了方便直接使用anular.js+ionic进行开发,里面有使用到echarts图表,具体开发中发现echarts在初始化绑定图表的DOM节点时,一直提示该节点不合法;可是明明已经把代码写在了window.onload()中了,又改成$(function(){})结果还是不行. 二,解决方案 1使用angular.element <script type="text/javascript"> angular.element(wi

  • js中匿名函数的创建与调用方法分析

    本文实例分析了js中匿名函数的创建与调用方法.分享给大家供大家参考.具体实现方法如下: 匿名函数就是没有名字的函数了,也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数.最经常用作回调函数(callback)参数的值,很多新手朋友对于匿名函数不了解.这里就来分析一下. function 函数名(参数列表){函数体;} 如果是创建匿名函数,那就应该是: function(){函数体;} 因为是匿名函数,所以一般也不会有参数传给他. 为什么要创建匿名函数呢?在什么情况下会使用到匿

  • 浅析JS中对函数function的理解(基础篇)

    正文:我们知道,在js中,函数实际上是一个对象,每个函数都是Function类型的实例,并且都与其他引用类型一样具有属性和方法.因此,函数名实际上是指向函数对象的指针,不与某个函数绑定.在常见的两种定义方式(见下文)之外,还有一种定义的方式能更直观的体现出这个概念: var sum = new Function("num1", "num2", "return num1 + num2"); //不推荐 Function的构造函数可以接收任意数量的参

  • 深入理解js 中async 函数的含义和用法

    一.终极解决 异步操作是 JavaScript 编程的麻烦事,麻烦到一直有人提出各种各样的方案,试图解决这个问题. 从最早的回调函数,到 Promise 对象,再到 Generator 函数,每次都有所改进,但又让人觉得不彻底.它们都有额外的复杂性,都需要理解抽象的底层运行机制. 异步I/O不就是读取一个文件吗,干嘛要搞得这么复杂?异步编程的最高境界,就是根本不用关心它是不是异步. async 函数就是隧道尽头的亮光,很多人认为它是异步操作的终极解决方案. 二.async 函数是什么? 一句话,

  • JS中for循环的四种写法示例(入门级)

    目录 1. 传统for循环 2. for of 循环 3. for in 循环 4. forEach方法 附完整实例: 总结 1. 传统for循环 for (init; cond; inc) { // body } 与C++或Java类似,for关键字后用小括号描述循环设置,在小括号中用两个分号;将循环设置隔断为三个部分,分别为: init初始化语句(指令),在整个循环开始前执行 cond条件(逻辑表达式),表示循环继续的条件 inc自增语句(指令),在每次循环体结束以后执行 整个循环的执行步骤

  • 对js中回调函数的一些看法

    最近在忙公司android的项目,所以也就很少抽时间来写些东西了.刚闲下来,我就翻了翻之前看的东西.做了android之后更加感觉到手机端开发的重要性,现在做native App  和Web App是主流,也就是说现在各种基于浏览器的web app框架也会越来越火爆了,做js的也越来越有前途.我也决定从后端开发渐渐向前端开发和手机端开发靠拢,废话不说了,我们来切入正题"js的回调函数"相关的东西. 说起回调函数,好多人虽然知道意思,但是还是一知半解.至于怎么用,还是有点糊涂.网上的一些

  • 关于原生js中bind函数的简单实现

    今天继续研究了bind函数的实现,也知道了shim和polyfill的说法,现在总结一下, if (!Function.prototype.bind) { Function.prototype.bind = function (oThis) { if (typeof this !== "function") { // closest thing possible to the ECMAScript 5 internal IsCallable function throw new Typ

  • JS中encodeURIComponent函数用php解码的代码

    JS中encodeURIComponent函数给中文编码后,如何用php解码?? 前提:编码前的中文可能是gbk,gb2312,utf-8等. 复制代码 代码如下: urldecode() iconv() 在JS中使用了encodeURIComponent对中文进行编码在PHP中使用iconv('UTF-8','gb2312',$q);就可以得到你需要的字串了,其中gb2312根据你实际应用来定如还不明白为什么看下面的文章 URL编码转换,escape() encodeURI() encodeU

随机推荐