5.深入理解箭头函数 - JS

什么是箭头函数?

箭头函数是指通过箭头函数表达式创建的函数,是匿名函数。

箭头函数表达式的语法更简洁,但语义有差异,所以用法上也有一些限制。尽管如此,箭头函数依旧被广泛运用在需要执行“小函数”的场景。

箭头函数表达式的基本语法

/* 函数体只有一个语句 */
() => 单个语句
单个参数 => 单个语句
(单个参数) => 单个语句
(参数1, 参数2) => 单个语句
let f = ()=>console.log(111)
f() === undefined		// true/* 函数体有多个语句,使用{} */
() => { 多个语句 }
单个参数 => { 多个语句 }
(单个参数) => { 多个语句 }
(参数1, 参数2) => { 多个语句 }
let g = ()=>{}

箭头函数表达式的语法说明

  1. 只有一个参数时,() 可以省略;
  2. 函数体只有单个语句,{} 可以省略,返回值就是该语句/表达式的值;
  3. 函数体有多个语句,必须使用 {} 包围;
  4. 语法上,返回一个对象字面量时,加 ()
  5. 箭头是复合符号,不要空格或换行;
  6. 参数列表和箭头之间不要换行;
  7. 箭头 => 的优先级不高,在符合运算中尽量用 () 包围。
let f = ()=>{ a:1 }			// 不报错,a被当成一个标签,执行了表达式 “1”,返回 undefined
let f = ()=>{ a: 1, b: 2 }	// 报错
let f = ()=>{ a: 1; b: 2 }	// 不报错,a,b都时标签
let f = ()=>( {a:1, b:2} )	// 不报错,()改变了优先级let g = () = > {}			// 报错
let g = () 					// 报错=> {}let x = f || ( ()=>{} )

箭头函数的限制:

  • 箭头函数不绑定 this,会捕获其声明所在上下文this,且 call/apply/bind 方法都不会改变其指向;
  • 不能用作构造函数,不能使用 new 调用,会引发类型错误,函数体内也无法访问 new.target
  • 不能使用 yield,不能用做生成器函数;
  • 没有 arguments 对象,可以使用剩余参数取而代之;
  • 没有原型对象 prototype 这个属性。

this 的绑定问题

箭头函数没有独立的 this,下面将依次总结其如何绑定上下文的 this、避免用作方法等限制问题。

绑定上下文的 this

箭头函数捕获声明所在上下文this 并绑定,考虑捕获绑定顺序如下:

  1. 外层函数:内部的箭头函数和外部(临近)的普通函数有相同的 this 绑定;
  2. :类具有封闭独立的 this 上下文,箭头函数作为方法时,将自动绑定实例的 this;本质就像闭包,有独立的变量环境,不会因为属性解构等操作改变其绑定。
  3. 全局:当以上不符合,一般和全局 globalThis 绑定。

由于箭头函数会绑定外部函数的 t h i s this this,所以在一些需要访问外部属性时常用,诸如函数 setTimeout/setInterval、方法 forEach/map 等。下面一个例子中:

  • showGroup2 内部的箭头函数绑定的是外部函数的 this,指向 w b 对象 wb对象 wb对象,所以能够访问其属性 group
  • showGroup3 绑定的是全局(globalThis),指向 全局对象 全局对象 全局对象,访问的是全局属性;
  • convert 内部的箭头函数绑定的是 this.datathis,指向 w b 对象 wb对象 wb对象,能够正确访问属性 group
  • convert2 内部的箭头函数绑定的也是 this.datathis,但外部也是箭头函数,指向 全局对象 全局对象 全局对象,将会报错或者得到错误的结果,所以尽量不使用箭头函数作为方法。
const wb = {group: '第一组',data: [1, 2, 3, 4],showGroup: function () { console.log(this.group); },showGroup2: function () { (() => { console.log(this.group) })(); },showGroup3: () => { console.log(this.group) },convert: function() {let res = new Array;this.data.forEach( (elem)=>{ res.push(this.group + ':' + elem); } );return res;},convert2: () => {let res = new Array;// console.log(this == globalThis),	// true// console.log(this.data);			// undefined/其他this.data.forEach( (elem)=>{ res.push(this.group + ':' + elem); } );return res;},
}

类的封闭性使得箭头函数与实例自动绑定,每创建一个实例,都分配一个闭包,和实例环境自动绑定。下面一个例子中:

  • 箭头函数 fa 绑定具体的实例对象,解构赋值不会改变 this 指向;
  • 普通方法 fd 在构造时绑定自身,解构赋值也不会改变 this 指向;
  • 另外的 fb/fc 作为普通方法,在构造时没有绑定自身,解构赋值会改变 this 指向。
class C {constructor() { this.fd = this.fd.bind(this) };a = 1;fa = ()=>{ console.log(this.a); };fb = function () { console.log(this.a); };fc () { console.log(this.a); };fd = function () { console.log(this.a); };
}let c = new C();
c.fa(); 					// 1
let { fa, fb, fc, fd } = c;
fa(); 						// 1
fb();						// 报错,属性未定义
fc();						// 报错,属性未定义
fd();						// 1

call、bind 和 apply 不会改变箭头函数的 this 指向

箭头函数表达式是执行完创建的,所以 this 绑定声明定义所在上下文的 this。callbindapply 方法都不会改变其指向。下面一个例子中:

  • 普通函数的 this 绑定,可以根据需求被 call/apply/bind 改变,指向 o o o 对象;
  • 箭头函数的 this 绑定,不可以call/apply/bind 改变,依旧指向全局对象。
const o = {	a: 10 }
globalThis.a = 100;const add = function (m, n) { return this.a + m + n; }
const add2 = n => this.a + m + n;add.call(o, 2, 3)			// 15
add.apply(o, [2, 3])		// 15
let boundedAdd = add.bind(o);
boundedAdd(2, 3)			// 15add2.call(o, 2, 3)			// 105
add2.apply(o, [2, 3])		// 105
let boundedAdd2 = add.bind(o);
boundedAdd2(2, 3)			// 105

在 setTimeout 中使用箭头函数

setTimeout 函数,使用一定封闭环境内的属性时,常用箭头函数作为回调函数。下面一个例子中:

  • loading 中箭头函数,绑定外部方法 loadingthis,指向 o 对象 o对象 o对象
  • loading2 中由 function 引导的声明式函数,将绑定全局(globalThis),指向 全局对象 全局对象 全局对象
  • setTimeout 不会影响内部箭头函数的 this 绑定,也不会影响内部普通函数的 this 绑定。
const o = {n: 0,loading () {setTimeout(() => {console.log(++this.n); // 箭头函数,this绑定声明所在上下文}, 1000)},loading2 () {setTimeout(function () {	console.log(++this.n); // 声明式函数,this绑定提升到全局}, 1000)},
}o.loading()		// 1秒后,控制台输出 1
o.loading()		// 再1秒后,控制台输出 2
o.loading2()	// 再1秒后,控制台输出 NaN 或其他

更一般的,在需要控制函数内 this 绑定指定区域时,采用箭头函数,一个例子如下:

const o = {n: 1,f() {let fn = function () { console.log(++this.n); }; // 声明式函数,this绑定提升到全局fn();},g() {let gn = () => { console.log(++this.n); };		// 箭头函数,this绑定声明所在上下文gn();},
}o.g()		// 2
o.f()		// NaN

不仅如此,箭头函数常出现在 promise 链中。或者说,箭头函数经常作为一个回调函数出现

没有 arguments 对象

箭头函数没有自己的 arguments 对象。类似 this 的绑定,箭头函数会使用上下文 / 外部的 arguments。下面一个例子中:

  • 内部箭头函数与外部函数 f 的参数隐式绑定;
  • arguments[0]f 的第一个参数 n
function f(n) {let g = () => arguments[0] + n; // f 的隐式参数绑定。arguments[0] 为 f 的第一个参数 nreturn g();
}f(3); // 3 + 3 = 6

JS 类没有默认的 arguments 字段,下面的例子中:

  • 全局箭头函数可以访问全局变量 arguments (如果存在);
  • 类可以自定义 arguments 字段并进行访问(使用 this 指定)。
globalThis.arguments = [1, 2, 3]
let f = () => { console.log(arguments[0]); }
class C {constructor(a, ...r) { this.arguments = [a, ...r]; };// arguments = [1, 2, 3];g = () => { console.log(this.arguments[0]); };
}f()						// 1
(new C(10, 1, 2)).g()	// 10

由于箭头函数的以上特性,在需要指定作用域的 this/arguments 时(setTimeout/Promise/闭包/装饰器/...),能够起到简化的作用。下面一个装饰器的例子中:

  • 内部箭头函数可以对外部函数的 thisarguments 访问,
function sayHi(name) { console.log(`Hello, ${name}!`); }function decorator1(fn, ms) {return function () {setTimeout(() => fn.apply(this, arguments), ms); }
}function decorator2(fn, ms) {return function (...args) {let nt = this;setTimeout(function() { return f.apply(nt, args); }, ms);}
}let f = decorator1(sayHi, 1000);
let g = decorator2(sayHi, 1000);
f('Lily');
g('Jack');

在大多情况下,使用剩余参比使用 arguments 是更好的选择

不能用作构造函数

  1. 箭头函数不能用作构造函数,当使用 new 调用时会出错。
const F = () => {this.a = 10;
}let f = new F()			// 报错,不能作为构造器
  1. 更不允许在箭头函数中使用 new.target
const G = (v) => {if (!new.target) return new G(v);	// 报错,不允许在箭头函数中出现 new.targetthis.k = v;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/681316.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Paper - VQGAN: Taming Transformers for High-Resolution Image Synthesis 简读

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/136055085 VQGAN: Taming Transformers for High-Resolution Image Synthesis, CVPR 2021 VQGAN: 改良 Transformer 模型以实现高清图像合成 源码…

TypeScript 入门

课程地址 ts 开发环境搭建 npm i -g typescript查看安装位置: $ npm root -g C:\Users\Daniel\AppData\Roaming\npm\node_modules创建 hello.ts: console.log("hello, ts");编译 ts 文件,得到 js 文件: $ tsc foo.…

LeetCode二叉树的垂序遍历

题目描述 给你二叉树的根结点 root ,请你设计算法计算二叉树的 垂序遍历 序列。 对位于 (row, col) 的每个结点而言,其左右子结点分别位于 (row 1, col - 1) 和 (row 1, col 1) 。树的根结点位于 (0, 0) 。 二叉树的 垂序遍历 从最左边的列开始直到…

ubuntu22.04@laptop OpenCV Get Started: 007_color_spaces

ubuntu22.04laptop OpenCV Get Started: 007_color_spaces 1. 源由2. 颜色空间2.1 RGB颜色空间2.2 LAB颜色空间2.3 YCrCb颜色空间2.4 HSV颜色空间 3 代码工程结构3.1 C应用Demo3.2 Python应用Demo 4. 重点分析4.1 interactive_color_detect4.2 interactive_color_segment4.3 da…

使用Docker,拉取Nginx镜像,创建并运行Nginx容器

查看DockerHub,拉取Nginx镜像,创建并运行Nginx容器。 在DockerHub中搜索Ningx镜像,查看镜像名称 拉取Nginx镜像 [rootservice ~]# systemctl start docker [rootservice ~]# mkdir -p /etc/docker [rootservice ~]# tee /etc/docker/daemo…

Imgui(1) | 基于imgui-SFML改进自由落体小球

Imgui(1) | 基于imgui-SFML改进自由落体小球 0. 简介 使用 SFML 做2D图形渲染的同时,还想添加一个按钮之类的 GUI Widget, 需要用 Dear Imgui。由于 Imgui 对于2D图形渲染并没有提供类似 SFML 的 API, 结合它们两个使用是一个比较好的方法, 找到了 imgui-SFML 这个…

C++,stl,map/multimap详解

目录 1.map的构造和赋值 2.map的大小和交换 3.map的插入和删除 4.map的查找和统计 5.map的排序 1.map的构造和赋值 #include<bits/stdc.h> using namespace std;void print(map<int,int> &mp) {for(map<int,int>::iterator it mp.begin(); it ! m…

数据库管理-第150期 Oracle Vector DB AI-02(20240212)

数据库管理150期 2024-02-12 数据库管理-第150期 Oracle Vector DB & AI-02&#xff08;20240212&#xff09;1 LLM2 LLM面临的挑战3 RAG4 向量数据库LLM总结 数据库管理-第150期 Oracle Vector DB & AI-02&#xff08;20240212&#xff09; 作者&#xff1a;胖头鱼的鱼…

2.8:Maefile、计算单词个数、判断文件类型、单词逆置

1.有main.c&#xff0c;test.c&#xff0c;test1.c&#xff0c;创建Makefile 程序代码&#xff1a; Makefile: 1 CCgcc2 EXEhello3 OBJS$(patsubst %.c,%.o,$(wildcard *.c))4 CFLAGS-c -o5 all:$(EXE)6 7 #hello依赖test.o main.o8 $(EXE):$(OBJS)9 $(CC) $^ -o $10 …

除了ajax还有什么方法获取数据而不用刷新数据

除了AJAX&#xff08;Asynchronous JavaScript and XML&#xff09;&#xff0c;还有以下几种方法可以在不刷新页面的情况下获取数据&#xff1a; Fetch API&#xff1a;Fetch API 是一个现代的网络 API&#xff0c;提供了一个 JavaScript Promise 对象来处理网络请求。Fetch …

腾讯云4核8G服务器多少钱?646元一年零3个月

腾讯云服务器4核8G配置优惠价格表&#xff0c;轻量应用服务器和CVM云服务器均有活动&#xff0c;云服务器CVM标准型S5实例4核8G配置价格15个月1437.3元&#xff0c;5年6490.44元&#xff0c;轻量应用服务器4核8G12M带宽一年446元、529元15个月&#xff0c;腾讯云百科txybk.com分…

算法学习——LeetCode力扣二叉树篇8

算法学习——LeetCode力扣二叉树篇8 669. 修剪二叉搜索树 669. 修剪二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 描述 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high…

[leetcode]买卖股票的最佳时机 (动态规划)

121. 买卖股票的最佳时机 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从…

恒创科技:香港 BGP 服务器网络连通性如何测试?

随着互联网的快速发展&#xff0c;网络连通性测试变得越来越重要。网络连通性测试的目的是确定网络设备之间的连接是否正常&#xff0c;以及数据包是否能够在网络中顺利传输。本文将介绍一种简单易行的香港 BGP 服务器网络连通性的测试方法&#xff0c;利用tracer测试工具。这里…

springboot184基于springboot的校园网上店铺的设计与实现

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

幻兽帕鲁 Linux 服务器迁移完成之后,进入游戏会出现闪退?怎么解决?

主要的原因是迁移的存档文件&#xff0c;新服务器可能没有操作存档文件的权限&#xff0c;不能成功更新存档&#xff0c;从而导致闪退。 建议&#xff1a;在 Linux 服务器内&#xff0c;依次运行如下命令后&#xff0c;再次尝试连接游戏&#xff1a; 第一步&#xff1a; s…

分布式认证JWT

JWT解释 JWT是一种加密后的数据载体&#xff0c;可在各应用间进行数据传输。 JWT的组成 包含3部分。header&#xff08;头&#xff09;、payload&#xff08;有效载荷&#xff09;、signature&#xff08;签名&#xff09;。格式是header.payload.signature Header组成 JWT…

JAVA-数组乱序

实现步骤 假设有一组数组numbers从数组中最后一个元素开始遍历设置一个随机数作为循环中遍历到的元素之前的所有元素的下标&#xff0c;即可从该元素之前的所有元素中随机取出一个每次将随机取出的元素与遍历到的元素交换&#xff0c;即可完成乱序 实例如下&#xff1a; im…

day39 Bootstrap——容器简括

前言 前言Bootstrap5 容器容器内边距容器的边框和颜色响应式容器 前言 Bootstrap&#xff0c;来自 Twitter&#xff0c;是目前最受欢迎的前端框架。Bootstrap 是基于 HTML、CSS、JAVASCRIPT 的&#xff0c;它简洁灵活&#xff0c;使得 Web 开发更加快捷。 Bootstrap5 容器 B…

2022年美赛A题个人思路

一、读题 自行车运动员获胜的机会不同&#xff0c;但取决于赛事的类型、路线和自行车运动员的能力。 能量曲线&#xff1a;表明自行车运动员可以产生给定能量的时间长短对于特定的时间长度&#xff0c;能量曲线提供了自行车运动员在给定时间内保持的最大能量。 &#xff08;1&a…