ES6系列之箭头函数全解析

引言

ES6中允许使用箭头=>来定义箭头函数,是ES6中较受欢迎也较常使用的新增特性。本文将从箭头函数的基本语法,与普通函数对比,箭头函数不适用场景三个方面进行梳理。

基本语法

// 箭头函数
let func = (name) => {
    // 函数体
    return `Hello ${name}`;
};

// 等同于
let func = function (name) {
    // 函数体
    return `Hello ${name}`;
};   

从上面可以看出,定义箭头函数语法上要比普通函数简洁得多。箭头函数省去了function关键字,采用箭头=>来定义函数。函数的参数放在=>前面的括号中,函数体跟在=>后的花括号中,箭头函数在参数和箭头之间不能换行。

箭头函数的参数

  1. 如果箭头函数没有参数,直接写一个空括号即可。
  2. 如果箭头函数的参数只有一个,可以省略包裹参数的括号。
  3. 如果箭头函数有多个参数,将参数依次用逗号分隔,参数必须被包裹在括号中。

箭头函数的函数体

如果箭头函数的函数体只有一句代码,即返回某个变量或者返回一个简单的JS表达式,可以省去函数体的大括号{ }。

let func = val => val;
// 等同于
let func = function (val) { return val };

let sum = (num1, num2) => num1 + num2;
// 等同于
let sum = function(num1, num2) {
  return num1 + num2;
};

let mulFunction = (num1, num2 ,num3) => num1 * num2 * num3;
// 等同于
let mulFunction = function(num1, num2 ,num3) {
    return num1 * num2 * num3;
}

箭头函数返回一个对象

如果箭头函数的函数体只有一句代码且返回一个对象(对象字面量)时,直接写一个表达式是不行的。

let func = () => { foo: 1 }; 
console.log(func()); // 执行后返回undefined

// 如果是这样还会直接报错
let func = () => { foo: 1, bar: 2 };

原因是花括号被解释为函数体的大括号,解决办法:用圆括号把对象字面量包起来

let func = () => ({ foo: 1 });
console.log(func()); // {foo: 1}

// 不过上面那样解决的缺点是可读性变差了,所以更推荐直接当成多条语句的形式来写,可读性高  
let func = () => {
    return {
        foo: 1
    }
}

简化回调函数

这是箭头函数比较常见的用法

// 普通函数写法
[1, 2, 3, 4].map(function (x) {
    return x * x;
});

let result = [5, 4, 1, 3, 2].sort(function (a, b) {
    return a - b;
});

// 箭头函数写法
[1, 2, 3, 4].map(x => x * x);

let result = [5, 4, 1, 3, 2].sort((a, b) => a - b);

跟普通函数的区别

1.没有this绑定

箭头函数没有自己的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就是Window对象。

2.没有arguments

function func1(a, b) {
    console.log(arguments);
}
let func2 = (a, b) => {
    console.log(arguments);
}
func1(1, 2); // Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
func2(1, 2); // Uncaught ReferenceError: arguments is not defined

如果非要打印函数参数,可以在箭头函数中使用rest参数代替arguments对象

let func2 = (...rest) => {
    console.log(rest); // (2) [1, 2]
}

3.不能通过 new 关键字调用

在构造函数中,this指向新创建的对象实例

而箭头函数没有 [[Construct]]方法,箭头函数不可以当作构造函数,如果这样做会抛出异常

var Person = (name) => {
    this.name = name;
}

// Uncaught TypeError: Person is not a constructor
var person = new Person('jacky'); 

箭头函数在创建时this对象就绑定了,故不会指向对象实例。

4.没有 new.target

new.target是ES6新引入的属性,普通函数如果通过new调用,new.target会返回该函数的引用。

function Cat() {
    console.log(new.target); 
}
let cat = new Cat(); // ƒ Cat() { console.log(new.target); }

此属性主要:用于确定构造函数是否为new调用的。

箭头函数的this指向全局对象,在箭头函数中使用箭头函数会报错。

// 普通函数
let a = function() {
    console.log(new.target);
}
a(); // undefined

// 箭头函数
let b = () => {
    console.log(new.target); // 报错:Uncaught SyntaxError: new.target expression is not allowed here
};
b();

5.没有原型

由于不能通过 new 关键字调用,不能作为构造函数,所以箭头函数不存在 prototype 这个属性。

let func = () => {};
console.log(func.prototype) // undefined

6.没有 super

箭头函数没有原型,故也不能通过 super 来访问原型的属性,所以箭头函数也是没有 super 的。同this、arguments、new.target 一样,这些值由外围最近一层非箭头函数决定。

7.call/apply/bind方法无法改变箭头函数中this的指向

call()、apply()、bind()方法的共同特点是可以改变this的指向,用来动态修改函数执行时this的指向。但由于箭头函数的this定义时就已经确定了且不会改变。所以这三个方法永远也改变不了箭头函数this的指向。

var name = 'global name';
var obj = {
    name: 'jacky'
}
// 箭头函数定义在全局作用域
let func = () => {
    console.log(this.name);
};

func();     // global name
// this的指向不会改变,永远指向Window对象,放到到window下的全局变量
func.call(obj);     // global name
func.apply(obj);    // global name
func.bind(obj)();   // global name

8.箭头函数的解析顺序相对靠前

虽然箭头函数中的箭头不是运算符,但箭头函数具有与常规函数不同的特殊运算符优先级解析规则。

let callback;

callback = callback || function() {}; // ok

callback = callback || () => {};      
// SyntaxError:非法箭头函数属性

callback = callback || (() => {});    // ok

9.箭头函数不支持重名参数

function foo(a, a) {
    console.log(a, arguments); // 2 Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}

var boo = (a, a) => { // 直接报错:Uncaught SyntaxError: Duplicate parameter name not allowed in this context
    console.log(a);
};
foo(1, 2);
boo(1, 2);

10.使用 yield 关键字

yield 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作生成器( Generator )。

箭头函数不适用的场景

1.不应被用在定义对象的方法上

var obj = {
  x: 10,
  b: function() {
    console.log( this.x, this)
  },
  c: () => console.log(this.x, this)
}
obj.b(); // 10  {x: 10, b: ƒ, c: ƒ}

obj.c(); // undefined Window

因为它内部this的指向原因,当使用obj.c()的时候,我们希望c方法里面的this指向obj,但是它却指向了obj所在上下文中的this(即window),违背了我们的需求,所以箭头函数不适合作为对象的方法。

2.具有动态上下文的回调函数,也不应使用箭头函数

var btn = document.getElementById('btn');
btn.addEventListener('click', () => {
  console.log(this);
});
为btn的监听函数是一个箭头函数,导致里面的this就是全局对象,而不符合我们想操作按钮本身的需求。如果改成普通函数,this就会动态指向被点击的按钮对象

除了前面两点,剩下的跟上面讲的与普通函数的区别重复了,故只作总结不贴代码了:

  1. 不应被用在定义对象的方法上
  2. 具有动态上下文的回调函数,也不应使用箭头函数
  3. 不能应用在构造函数中
  4. 避免在 prototype 上使用
  5. 避免在需要 arguments 上使用
Image placeholder
fander
未设置
  58人点赞

没有讨论,发表一下自己的看法吧

推荐文章
ES6系列之变量的解构赋值

1.什么是解构?ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。它在语法上比ES5所提供的更加简洁、紧凑、清晰。它不仅能减少你的代码量,还能从根本上改变你的编码方式。2.数

PHP 7.4 新语法:箭头函数

短闭包,也叫做箭头函数,是一种用php编写的短函数.当向函数中传递闭包时,这个功能是非常有用的,比如使用array_map或是array_filter函数时. 这就是它们看起来的样子: //Post

干货丨爱奇艺CDN IPv6系统配置

1.背景  IPv6是“InternetProtocolVersion6”(互联网协议第6版)的缩写,是互联网工程任务组(IETF)设计的用于替代IPv4的下一代IP协议。IPv4地址资源紧缺严重制约

ES6问答-async函数

async函数 async是什么?它和Genernator函数外观上的区别是什么? constasyncReadFile=asyncfunction(){ constf1=awaitreadFile

微服务配置中心完全解读

本文作者:风卿,Nacos社区committer.在撰写这篇技术选型的文章之前,是比较犹豫的。因为,以其中一个开源项目开发者的身份,去写一篇三个开源项目的对比,即便很克制的去客观的比较,也很难有信服力

【CSS全解01】CSS基础-体系化学CSS

大纲 基础部分学习占比:HTML1%`CSS19%Javascript80%`(`基础部分?%框架?%`项目?%) CSS历史 AcidTestforbrowser CSS是艺术(非逻辑,用测试经验来

TPC-C解析系列03_TPC-C基准测试之SQL优化

TPC-C是一个非常严苛的基准测试模型,考验的是一个完备的关系数据库系统全链路的能力。这也是为什么在TPC-C的榜单前列,出现的永远只是大家熟知的那几家在业界有着几十年积累、从关系数据库理论开始发展就

TPC-C解析系列05_TPC-C基准测试之存储优化

TPC-C规范要求被测数据库的性能(tpmC)与数据量成正比。TPC-C的基本数据单元是仓库(warehouse),每个仓库的数据量通常在70MB左右(与具体实现有关)。TPC-C规定每个仓库所获得的

TPC-C解析系列01_TPC-C benchmark测试介绍

作者:阳振坤2019.10导语:自从蚂蚁金服自研数据库OceanBase获得TPC-C测试第一名后,引起了行业内外大量关注,我们衷心的感谢大家对OceanBase的支持与厚爱,也虚心听取外界的意见和建

TPC-C解析系列02_OceanBase如何做TPC-C测试

导语:蚂蚁金服自研数据库OceanBase登顶TPC-C引起业内广泛关注,为了更清楚的展示其中的技术细节,我们特意邀请OceanBase核心研发人员对本次测试进行技术解读,共包括五篇:1)TPC-C基

TPC-C解析系列04_TPC-C基准测试之数据库事务引擎的挑战

OceanBase这次TPC-C测试与榜单上Oracle和DB2等其他数据库在硬件使用上有非常大的不同,OceanBase的数据库服务器使用的是204+3台型号是ecs.i2.16xlarge阿里云E

使用 ES6 写 Koa Web 项目

完整代码:传送门我们node.js只是实现了部分ES6的语法,所以为了让我们ES6的代码能100%在node.js下执行,必须使用我们的babel把ES6代码编译成nodejs可执行的代码。node环

“设备+云管”双管齐下 H3C WAS6100交换机评测

俗话说“好马配好鞍,好船配好帆”。搭配是门技术活,对于企业组网来讲亦是如此。现如今,网络已成为企业业务的主要动脉,网速的快慢直接影响公司的办公效率,网络安全问题也成为公司最大的威胁。受制于成本和规模,

dw cs6怎么保存为css格式

dwcs6怎么保存为css格式1、首先,打开DreamweaverCS6,新建一个css文档,选中“css”,点击确定按钮。2、将设计好的css样式放入该页面,@charset"utf-8";用来指定

【Kubernetes系列】第5篇 Ingress controller – traefik组件介绍

1.概述为了能够让Ingress资源能够工作,在Kubernetes集群中必须至少有一个运行中的ingresscontroller组件。也就是说如果在kubernetes集群中没有一个ingressc

微信支付退款解析 对加密串 B 做 AES-256-ECB 解密(PKCS7Padding)

微信支付退款解析对加密串B做AES-256-ECB解密(PKCS7Padding)1.微信支付文档 https://pay.weixin.qq.com/wiki/doc/api/H5....解密方式解

nodejs怎么解析less?

nodejs怎么解析less?在cmd中使用npm中的less模块来解析less。npm(全称NodePackageManager,即“node包管理器”)是以JavaScript编写的软件包管理系统

Stylus系列——webpack-spritesmith配合stylus使用示例

一、前言基于Webpack的CSSSprites实现方案,若是直接在html中调用雪碧图图标已经很方便,但是实际开发过程可能遇到需要在伪元素中使用雪碧图,或者需要hover切换另一个图标,这种情况下就

基于Go的语义解析开源库FMR,“屠榜”模型外的NLP利器

如何合理地表示语言的内在意义?这是自然语言处理业界中长久以来悬而未决的一个命题。 在2013年分布式词向量表示(DistributedRepresentation)出现之前,one-hot是最常用的

从 simplemde 写入 + inline-attachment 图片拖拽上传 到 parsedown 解析

###准备工作安装富文本编辑器sparksuite/simplemde-markdown-editor yarnaddsimplemde--save 安装markedjs/marked,在JS中解析

SQL 查询语句的执行顺序解析

代理搭建-shadowsockshttps://www.jetbrains.com/shop/eform/opens...https://blog.csdn.net/lianghecai5217131

用 Go 构建一个 SQL 解析器

在本文中,小编将向大家简单介绍如何在Go中构造LL(1)解析器,并应用于解析SQL查询。希望大家能用Go对简单的解析器算法有一个了解和简单应用。摘要本文旨在简单介绍如何在Go中构造LL(1)解析器,在

Spring Cloud 上手实战-架构解析及实作

Spring简介为什么要使用微服务单体应用:目前为止绝大部分的web应用软件采用单体应用,所有的应用的用户UI、业务逻辑、数据库访问都打包在一个应用程序上。缺点:开发相互干扰,随着应用的不断升级沟通协

基于Go的语义解析开源库FMR,“屠榜”模型外的NLP利器

(由AI科技大本营付费 下载自视觉中国)作者|刘占亮一览群智技术副总裁编辑|Jane出品|AI科技大本营( ID:rgznai100)如何合理地表示语言的内在意义?这是自然语言处理业界中长久以来悬而未