结合工作中使用情况,简单对es6进行一些复习总结,包括常用的语法,api等,以及短时间内要上手需要重点学习的知识点(不同工作环境可能有一些差别),主要参考链接是阮一峰的博客以及外文博客(阮老师大部分文章是直接翻译的这个博客) http://2ality.com/p/about.html
1.简介
先说一下ECMAScript,ECMA是一个标准化组织,他们制定了JavaScript的语言标准,所以之后一直称为ECMAScript,广泛的讲,Javascript是其一种标准实现,包括ECMAScript,dom,bom等
ECMA组织每年都会有新标准发布,标准名为ES+年份,所以我们会听到ES2015,ES2016,ES2017等,ES6其实就是ES2015,表示在15年发布的最新版本,相应的ES2016,ES2017分别称为ES7和ES8,依次往后对应。
现在大部分博客,文章,分享中提到的ES6其实也可以理解为ES6+,只不过因为2015年之前,ECMAScript有很多年没有发版(也有历史原因:ES4的夭折),积累了大量的新语法,api,所以在2015年一次性加在了ES6版本中发布,之后每年都只有很少的一些新增,比如:
- ES7: http://2ality.com/2016/01/ecm... (只有很少的两个属性)
- ES8:http://2ality.com/2016/02/ecm...
- …
语法提案
关于语法提案的流程,简单了解一下:一个新的语法从提案到成为标准需要经过五个阶段,每个阶段都需要标准委员会TC39定时开会进行批准
流程见链接:http://exploringjs.com/es2016...
需要注意的一点,所有的标准都是基于实现的,几大厂商浏览器对提案的实现必须先于标准才会进入定稿阶段,包括Babel
我们常用的async和await,就是因为16年开会的时候没有达到可定案的标准,而没有进入ES2016标准,最后添加在2017标准中
可参考该链接,有每年发布标准中新增的特性:http://2ality.com/2017/02/ecm...
2.简单知识点介绍
下面主要就是结合平时工作,简单介绍最常用的语法,类方法以及实例方法等,下面主要是一些列举,大部分知识点想深入学习的话,还是得到阮老师博客或者外文博客上学习
不过我标注了实际工程中的使用频率,可以参考,针对性的学习,快速上手
提示:实际开发中结合eslint可以帮助自己写出更规范的代码
使用频率极高(为方便一起介绍,会将一些不常用的语法也列出来,标题括号中也为大量使用):
2.1 let与const
这是最基础的两个命令,都是声明变量,要强制所有的声明都使用他们两个
与var的区别:
-
作用域
- var作用域在全局
- let与const作用域为代码块
- 变量提升 (https://www.jianshu.com/p/68a...)
- 暂时性死区,表示在一个块级作用域内,只要使用let声明一个变量,声明前将不能使用该变量
- 不允许重复声明
- 与顶层对象window或者global的关系,var声明的全局变量与顶层变量挂钩,而let const声明的全局变量不挂钩
const与let区别:
- const定义一个只读的常量,一旦声明不可改变,但需要注意的是,不可改变的是变量指向的那个内存地址,如果使用const声明一个对象,对象内部是可以改变的,如果要强制一个对象不可改变,那么可以使用Object.freeze来冻结(该方法只冻结该对象,不会冻结其属性,所以可考虑递归)
2.2 默认值设置
我们可以直接在通过 param = 1 的形式设置默认值
包括为函数参数设置,为解构设置(见下面介绍)
避免了以前的这种写法:
function test(data) {
data = data || {};
...
}
// 可以直接写为
function test(data = {}) {
...
}
需要注意的是:默认值生效的条件是,严格等于 undefined,如果值为null,不会使用默认值
2.3 解构赋值与rest操作符
解构
解构赋值其实就是为了更方便的从数组或者对象中通过属性名取数据,熟练使用之后,配合默认值,rest操作符可以很大程度提高效率,简化代码
下面是一些简单的使用对比
var a = 1, b = 2, c = 3;
// 或者
var param = { a: 1, b: 2, c: 3 };
var a = param.a || 1; // 默认值
var b = param.b;
var c = param.c;
// 或者
function add(param) {
param = param || {};
var v1 = param.v1 || 0;
var v2 = param.v2 || 0;
return v1 + v2;
}
可以看出来,如果参数比较多的话,代码很臃肿
如果用解构赋值,我们可以写成这样,很简洁
let [a, b, c] = [1, 2, 3]
// 或者
let {a = 1, b, c} = {a: 1, b: 2, c: 3}
// 或者
function add({v1 = 0, v2 = 0} = {}){
return v1 + v2;
}
// 类比上面代码,其中 {v1 = 0, v2 = 0} 这个对象接收param参数,如果param未传入,设置默认值为{},进而,v1,v2从param或者默认值{}中取值,取不到的话也设置默认值
解构的核心是,=号左边通过属性名直接获取=号右边对象相应的属性
并且所有类型的数据都可以被解构,即出现在=号右边,只不过字符串,数字,布尔值出现在等号右边时会先转化为对象
使用解构可以大大改善代码的可读性,减少的冗余的代码,更灵活简洁的获取属性,设置默认值,尤其是配合rest操作符时。
rest操作符,即…
使用在函数参数中(取代arguments)或者解构赋值时,方便我们批量的获取设置数据,大量用在数组和对象中,见下面 数组部分
该语法在ES2018中成为规范,但是工作中早已大量使用,参考博客 http://2ality.com/2016/10/res...
注意:...只能出现一次并且必须出现在结尾
下面是错误的用法:
const {...rest, foo} = obj; // SyntaxError
const {foo, ...rest1, ...rest2} = obj; // SyntaxError
2.4 函数相关的扩展(箭头函数,rest参数,默认值)
ES6对函数进行了很多扩展,其中最常用的是箭头函数以及rest参数和默认值,rest参数与默认值都与解构关系密切
2.4.1 默认值
- 一般在函数中设置默认值的参数应该是尾参数
- 如果指定了默认值,函数的length值为默认值参数前面未使用默认值的参数个数
注意:函数参数值会形成一个新的作用域,区别于函数内作用域
2.4.2 箭头函数
这个使用频率极高,大大简洁了代码,尤其作为回调传入的时候
与es5函数区别:
- 自动绑定函数定义时作用域this,替换bind写法(在需要手动绑定this时,使用箭头函数)
- 不能作为构造函数
- 没有arguments,使用rest代替
下面可选择性了解:
2.4.3 函数 name属性,会返回函数名
- 可能用到的,使用bind绑定之后,name属性前会加bound前缀
2.3.4 函数式编程中尾调用和尾递归,大大优化内存,待深入
2.5 字符串扩展(模板)
常用的主要是以下几点
最常用的是支持模板写法,使用反引号``和${} 取代字符串拼接
// 比如:
const a = ‘222’, b = ‘333’;
const c = a + ‘444’ + b;
const c = `${a}444${b}`
剩下的选择性了解:
- 加强对unicode支持,使用频率很小
- 支持for of 遍历,使用频率小,可使用split或者rest代替
- 增加搜索方法,includes,startsWith,endsWith,之前只有一个indexOf
- repeat,padStart,padEnd 循环或者补全字符串,偶尔使用
2.6 数组扩展(rest操作符,实例方法)
主要是增加以下常用点
-
…操作符,类rest
- 替代apply写法,如下,还有很多比如max,min求值,push一个数组等等
- 替代concat
- clone
- 结合解构赋值等
- string -> array,替换split写法,并且可识别4字节的unicode
- 将iterator接口的对象转化为数组,比如dom获取到的列表,任何部署了Iterator接口的类数组对象都可以,map,set对象等
-
实例方法,比如coryWithin,find,findIndex,fill,entries,keys,values,includes等
- find,findIndex,includes相比indexOf,可以识别NaN,并且更语义化
let a = [1, 2, 3], b = [2, 3, 4];
let merge = […a, …b] // [1, 2, 3, 2, 3, 4]
// 替换apply
function func (a, b, c) {…}
// 如果需要传入一个数组,es5就要这样写
func.apply(null, [1, 2, 3])
// es6
func(…[1, 2, 3])
// 反转字符串的实现
[…str].reverse().join(‘')
// 类数组对象
{
‘0’: ’12’,
‘1’: ‘123’,
length: ‘2'
}
下面的选择性学习:
-
Array.form 将类数组对象或者实现了iterator接口对象转化为数组,常见的有dom集合,arguments,map,set等,使用频率一般
- 区别于…操作符的是,不仅能转化iterator,还能转化类数组对象,如上代码
- 可传入第二个参数,类似于map方法
- Array.of 将一组值转化为数组,替代 Array() 或者 new Array(),使用频率一般
2.7 对象扩展(遍历与简化写法)
简化对象写法,新增一些Object类方法,使用频率极高
主要更新
-
简化对象写法,工作中应该全面改写
- 比如{x, y} 等同于{x: x, y: y}
- 函数类似 {func() {}} 等同于 {func: function() {}}
- 新增keys,values,entris方法,获取对象的属性名数组,值数组或者键值对,使用较多,可配合for of使用(必须是可遍历对象)
- Object.is 类似于 ===,但是可识别 isNaN === isNaN // true 与+0 === -0 // false,与我们主观意识一致
- Object.assign 合并对象的可枚举属性,后者会覆盖前者,属于浅拷贝
-
属性的遍历:
- for in 遍历自身以及继承的可枚举属性
- Object.keys 自身的可枚举属性键名
- getOwnPropertyNames 自身所有属性,包括不可枚举
- getOwnPropertySymbols 获取自身所有symbol属性键名,前三者都不包括symbol
- Reflect.ownKeys 所有的自身属性
下面的选择性学习:
- 属性名可以用表达式
- 提供一些对原型链的操作,使用极少,有兴趣可研究
- 新增super字段,区别于this,this指向该对象,super指向原型对象
3.高级知识点
对上面使用频率高的知识点进行了解学习以后便可以进行基本开发了,熟练使用后自然会体会到es6的美
但是实际工作中,不可能都是这些基础语法,api,需要用到更多的高级知识点
目前我们在大量使用的有:
这儿做简单介绍,工作之余可以自己去深入,其中Class好理解,Module和Decorate相对也好上手
主要是Promise与Async,简单了解之后也可以快速写代码,但是当应用复杂时,还是需要你深入了解其工作原理
推荐首学Promise,Decorator可在用到时再学
3.1 Promise
这是重中之重,ES6提出的异步的一种解决方案,要深入学习
3.2 async与await
也是异步的解决方案,代替Generator,更友好,语义化的使用
3.3 Class
ES6提供的语法糖,内部使用prototype实现,以面向对象的方式实现JS
3.4 Decorate
装饰器,可以对类或者类方法进行装饰,增加一些额外的行为
3.5 Module
模块化的实现,帮助我们拆分组合代码,模块化开发
此处还有两个概念,CommonJS和AMD模块
4.使用频率低的一些特性:
除了前面介绍的,剩下就是一些工作中用到比较少的特性,包括一些你常用但是并不知道的底层技术,比如Iterator,当你使用 … 或者for of时都会用到该知识点,建议学习
4.1 Set与Map
新增的数据结构,与Java中类似,目前来看,习惯未养成,使用比较少,但是建议学习
4.2 正则的扩展
工作中会常用的正则语法便可以了,推荐一个链接,帮助你更好的学习使用正则:https://regexper.com/#%2Fabc%...
有时候可以用先行断言和后行断言实现一些复杂的判断
- 字符串可使用正则的方法:match,replace,search,split,在es6环境下内部都是使用正则的方法来进行匹配,可以将replace第二个参数传入函数来观察函数参数,比如
- 新增u修饰符,可识别大于uffff的unicode字符
- 新增y修饰符,类似于g,但是下次匹配必须从剩余字符串的头部开始,对应属性为sticky
- 新增flags属性,返回修饰符
- 新增dotAll模式,见链接http://2ality.com/2017/07/reg...,.可以匹配任意单个字符,之前不匹配终止符,n r 等
- 支持后行断言,介绍见笔记本->正则
- 新增具名组匹配,同上
- matchAll一次性返回所有的匹配,返回结果是一个遍历器
4.3 symbol
es6新增的原始数据类型,表示独一无二的值,一般使用为直接Symbol(str),Symbol.for(str),Symbol.keyFor(str)
现在有string boolean,number,undefined,null,symbol六种,JS语言内部大量使用,工作中偶尔会用,比如定义私有属性的时候
const FETCH = Symbol('fetch');
import fetch from './lib/fetch';
module.exports = {
get fetch() {
if (!this[FETCH]) {
this[FETCH] = fetch;
}
return this[FETCH;
},
}
4.4 Proxy与Reflect,都是JS语言层次上的概念
Proxy即在目标对象外设置一层代理,外界真正访问到该对象时必须先访问代理
Reflect类似Proxy,是ES6为了操作对象而新增的Api,将一些Object上的对象放在了Reflect对象上,并修改了一些行为
4.5 数值方面的扩展
这部分主要是新增方法,使用频率总体不高
- 新增 0b 0o表示二进制和八进制,es5八进制表示为0开头(严格模式下不可用)
-
Number新增类方法,
- isFinite与isNaN,与挂载在全局的两个方法区别是参数只能是数值,方法内部不会调用Number()转化
- parseInt与parseFloat,与全局保持一致
- isInteger,参数只能为数值并且 25与25.0一样,并且不在js精度范围内时会误判
- 常量EPSILON,js能够识别的最小误差
- Math对象新增大量方法,很少用
- ** 指数运算,与Math.pow类似,对于特大运算,可能有误差