## 一、箭头函数
  箭头函数(Arrow Function)是ES6提供的一个很实用的新功能,与普通函数相比,不但在语法上更为简洁,而且在使用时也有更多注意点,下面列出了其中的三点:
  (1)由于不能作为构造函数,因此也就没有元属性(new.target)和原型(prototype属性)。
  (2)函数体内不存在arguments、super和this,即没有为它们绑定值。
  (3)当需要包含多个参数时,它们的名称不可重复。
**1)语法**
  箭头函数由三部分组成:箭头(=>)、参数和函数体,省略了名称和function关键字,如下代码所示。注意,参数和箭头要在一行中,不能换行,并且没有箭头函数声明,只有箭头函数表达式。
~~~
var arrow1 = (name, age) => {
return name;
};
~~~
  如果函数体中只包含一个表达式,那么可以像下面这样省略花括号和return关键字。
~~~
var arrow2 = (name, age) => name;
~~~
  当返回值是一个对象字面量时,需要为其包裹圆括号(如下代码所示),以免将花括号解析成代码块而引起语法错误。
~~~
var arrow3 = (name, age) => ({name: name, age: age});
~~~
  如果只有一个参数,那么可以省略包裹参数的圆括号。
~~~
var arrow4 = name => name;
~~~
  在参数缺失的情况下,还是需要一对空的圆括号占位。
~~~
var arrow5 = () => "strick";
~~~
  箭头函数也支持ES6新增的参数解构、剩余参数和默认参数,依次如下所示。
~~~
var arrow6 = ({name, age}) => name + age;
var arrow7 = (name, ...args) => name + args[0];
var arrow8 = (name = "strick") => name;
~~~
  箭头函数很适合那些只需做简短计算的函数,例如数组排序的回调函数。下面代码的第一段采用了普通的匿名函数,第二段采用了箭头函数,可以看出第二段的代码来得清晰简洁,更有吸引力。
~~~
var list1 = [1, 2, 3].sort(function(a, b) {
return a < b;
});
var list2 = [1, 2, 3].sort((a, b) => a < b);
~~~
  虽然箭头不是运算符,但是当把它与其它运算符结合的时候,需要将其用圆括号隔离,否则会引起语法错误。
~~~
var operator1 = 0 || () => 2; //语法错误
var operator2 = 0 || (() => 2); //() => 2
var operator3 = 0 || (() => 2)(); //2
~~~
  上面代码的第一句是错误的写法;第二句中的箭头函数默认是不会自动执行的,因此或运算的结果是函数本身;第三句在箭头函数的末尾又加了一对圆括号,从而创建了立即执行函数表达式(IIFE),运算后的结果是2。
  普通函数能创建两种即时函数(如下代码所示),区别就是末尾的那对圆括号的位置,相对于函数是在内还是在外,而箭头函数只支持第一种写法的即时函数。
~~~
(function() {})(); //第一种
(function() {}()); //第二种
~~~
**2)this**
  普通函数都会包含一个this对象,而this绑定的对象只取决于函数的调用方式。在下面的代码中,有两个函数,分别是构造函数people()以及定时器的回调函数。
~~~
function people() {
var self = this;
this.age = 28;
setTimeout(function() {
this.age; //undefined
self.age; //28
}, 100);
}
new people();
~~~
  people()中的this通过构造函数绑定,指向了新创建的目标对象。而回调函数中的this通过默认绑定,指向了全局对象(window)。由于age属性只存在于构造函数中,因此在全局对象中是访问不到的。如果要在定时器的回调函数中访问age属性,那么需要为this赋予一个别名(例如代码中的self)。
  前面曾提到过,箭头函数中不包含this,不过在其内部还是能使用this的,这个this是从上一层的作用域中继承而来的。下面将构造函数people()改造成animal(),定时器采用箭头函数。
~~~
function animal() {
this.age = 28;
setTimeout(() => {
this.age; //28
}, 100);
}
new animal();
~~~
  此时,箭头函数中的this指向的是新创建的目标对象,因此能成功访问到age属性。
  普通函数可以通过bind()、apply()或call()方法来更改this的指向,但这不适用于箭头函数,如下代码所示。还是以animal()为例,在箭头函数上调用call(),虽然第一个参数传入了全局对象,但依然无法改变this的指向。
~~~
function animal() {
this.age = 28;
setTimeout((() => {
this.age; //28
}).call(window), 100);
}
new animal();
~~~
## 二、尾调用优化
  尾调用(Tail Call)是指函数中的最后一步会调用一个函数,并将其结果作为返回值,就像下面这两个函数。
~~~
function tail1() {
return digit(28);
}
function tail2(name) {
if (name == "strick")
return digit(28);
return digit(30);
}
~~~
  如果在最后一步调用的是自身,那么这种情况可称为尾递归(Tail Recursion)。下面是个会无限递归的函数,只是为了做演示用。
~~~
function recursion() {
return recursion();
}
~~~
  尾调用不是一个新语法,而是一条组织代码的约束。下面代码里的三种情况就都不符合尾调用的要求。
~~~
function error1() {
digit(28);
}
function error2() {
return digit(28) + 1;
}
function error3() {
var number = digit(28);
return number;
}
~~~
  error1()函数缺少return关键字;error2()函数在调用digit(28)之后还额外多做了一次加法运算;error3()函数没有直接返回digit(28)的结果,而是先赋给一个变量,再返回。
  接下来为了能更好的理解尾调用优化,会先讲解调用栈的基础知识。
**1)调用栈**
  简单地说,调用栈(Call Stack)是一种记录函数地址的栈,当调用一个函数时,会在调用栈的顶端添加一个新的栈帧(Stack Frame),即入栈;而当函数执行完毕后,就会将其移出调用栈,即出栈。
  正常情况下,调用栈能够保持有进有出的平衡,但当函数的调用次数激增时(例如过多的递归),调用栈就会消耗大量内存,甚至占满整个内存空间,从而导致栈溢出(Stack Overflow)。不过值得庆幸的是,现代浏览器都不会因为栈溢出而奔溃,其内部已做了限制,例如Chrome仅仅会给出“RangeError: Maximum call stack size exceeded”的错误警告。
**2)尾调用优化**
  为了控制栈帧的数量,减少内存空间的使用,引入了尾调用优化(Tail Call Optimization,TCO)。这也不是一个新语法,只是一种空间上的优化,并且所有的工作都由JavaScript引擎(例如V8、SpiderMonkey等)代劳了。
  ES6中的TCO只有在严格模式下才能开启,它的原理就是不需要记住尾调用所处函数的地址。下面是一个检测字符串回文的函数,当开启TCO时,由于函数内需要的值已通过参数的形式传递进来(即不依赖函数外部的变量),因此每次递归都会覆盖前一次函数调用的栈帧,即调用栈的数量会保持恒定。
~~~
"use strict";
function palindrome(str) {
if (str.length <= 1)
return true;
if (str[0] != str[str.length - 1])
return false;
return palindrome(str.substr(1, str.length - 2));
}
~~~
  虽然TCO的表现很抢眼,但很遗憾,目前除了Safrai之外,大部分浏览器暂不支持(可参考[ES6浏览器兼容性](http://kangax.github.io/compat-table/es6/)),因此在这些浏览器中还享受不到TCO带来的性能提升。
*****
> 原文出处:
[博客园-ES6躬行记](https://www.cnblogs.com/strick/category/1372951.html)
[知乎专栏-ES6躬行记](https://zhuanlan.zhihu.com/pwes6)
已建立一个微信前端交流群,如要进群,请先加微信号freedom20180706或扫描下面的二维码,请求中需注明“看云加群”,在通过请求后就会把你拉进来。还搜集整理了一套[面试资料](https://github.com/pwstrick/daily),欢迎浏览。
![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200)
推荐一款前端监控脚本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不仅能监控前端的错误、通信、打印等行为,还能计算各类性能参数,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、扩展运算符和剩余参数
- 3、解构
- 4、模板字面量
- 5、对象字面量的扩展
- 6、Symbol
- 7、代码模块化
- 8、数字
- 9、字符串
- 10、正则表达式
- 11、对象
- 12、数组
- 13、类型化数组
- 14、函数
- 15、箭头函数和尾调用优化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、类
- 21、类的继承
- 22、Promise
- 23、Promise的静态方法和应用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基础实践
- 3、WebRTC视频通话
- 4、Web音视频基础
- CSS进阶
- 1、CSS基础拾遗
- 2、伪类和伪元素
- 3、CSS属性拾遗
- 4、浮动形状
- 5、渐变
- 6、滤镜
- 7、合成
- 8、裁剪和遮罩
- 9、网格布局
- 10、CSS方法论
- 11、管理后台响应式改造
- React
- 1、函数式编程
- 2、JSX
- 3、组件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表单
- 8、样式
- 9、组件通信
- 10、高阶组件
- 11、Redux基础
- 12、Redux中间件
- 13、React Router
- 14、测试框架
- 15、React Hooks
- 16、React源码分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基础
- 4、webpack进阶
- 5、Git
- 6、Fiddler
- 7、自制脚手架
- 8、VSCode插件研发
- 9、WebView中的页面调试方法
- Vue.js
- 1、数据绑定
- 2、指令
- 3、样式和表单
- 4、组件
- 5、组件通信
- 6、内容分发
- 7、渲染函数和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、数据类型
- 2、接口
- 3、类
- 4、泛型
- 5、类型兼容性
- 6、高级类型
- 7、命名空间
- 8、装饰器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系统和网络
- 3、命令行工具
- 4、自建前端监控系统
- 5、定时任务的调试
- 6、自制短链系统
- 7、定时任务的进化史
- 8、通用接口
- 9、微前端实践
- 10、接口日志查询
- 11、E2E测试
- 12、BFF
- 13、MySQL归档
- 14、压力测试
- 15、活动规则引擎
- 16、活动配置化
- 17、UmiJS版本升级
- 18、半吊子的可视化搭建系统
- 19、KOA源码分析(上)
- 20、KOA源码分析(下)
- 21、花10分钟入门Node.js
- 22、Node环境升级日志
- 23、Worker threads
- 24、低代码
- 25、Web自动化测试
- 26、接口拦截和页面回放实验
- 27、接口管理
- 28、Cypress自动化测试实践
- 29、基于Electron的开播助手
- Node.js精进
- 1、模块化
- 2、异步编程
- 3、流
- 4、事件触发器
- 5、HTTP
- 6、文件
- 7、日志
- 8、错误处理
- 9、性能监控(上)
- 10、性能监控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 监控系统
- 1、SDK
- 2、存储和分析
- 3、性能监控
- 4、内存泄漏
- 5、小程序
- 6、较长的白屏时间
- 7、页面奔溃
- 8、shin-monitor源码分析
- 前端性能精进
- 1、优化方法论之测量
- 2、优化方法论之分析
- 3、浏览器之图像
- 4、浏览器之呈现
- 5、浏览器之JavaScript
- 6、网络
- 7、构建
- 前端体验优化
- 1、概述
- 2、基建
- 3、后端
- 4、数据
- 5、后台
- Web优化
- 1、CSS优化
- 2、JavaScript优化
- 3、图像和网络
- 4、用户体验和工具
- 5、网站优化
- 6、优化闭环实践
- 数据结构与算法
- 1、链表
- 2、栈、队列、散列表和位运算
- 3、二叉树
- 4、二分查找
- 5、回溯算法
- 6、贪心算法
- 7、分治算法
- 8、动态规划
- 程序员之路
- 大学
- 2011年
- 2012年
- 2013年
- 2014年
- 项目反思
- 前端基础学习分享
- 2015年
- 再一次项目反思
- 然并卵
- PC网站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端学习之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 日志
- 2020