ES2015-ES2020
October 31, 2019
1. TC39 工作流程 - 标准一般每年6-7月发布
- Stage 0 (Strawperson 稻草人)
- Stage 1 (Proposal 建议)
- Stage 2 (Draft 草案)
- Stage 3 (Candidate 候选)
- Stage 4 (Finished 完成)
2. ES2015(ES6):常用特性
- class
- ESM 模块化
- 箭头函数,函数参数默认值
- 模版字符串
- 解构赋值,数组rest/spread操作符
- 对象属性简写
- Promise, Generator 和 Iterator
- let 和 const, Map 和 Set
- 新增基本类型Symbol
- ...
全部特性点这里
2.1 模版字符串
常规用法
//字符串拼接
let a = '风';
let b = `${a},冷${a}, 冷${a}吹`;//'风,冷风,冷风吹'
b = `
${a}
冷${a}
冷${a}吹`;
/*风
**冷风
**冷风吹
*/
2.2 解构赋值,rest/spread操作符
// 右侧为spread赋值,左侧为rest接受赋值
let a = [1,2,3,4];
let b = [1,2,...a];
let [c,,d,...e] = b;
/*
b: [1,2,1,2,3,4]
c: 1
d: 1
e: [2,3,4]
*/
2.3 Symbol / Iterator / Generator
- Iterator: 实现next( )方法,返回 { value: xxx, done: true / false}
- Generator: 创建时返回iterator对象
- 字符串、数组、Map和Set是内置了迭代器的对象,原型内置 [Symbol.iterator] 方法
可以自己实现或者覆盖已有的 [Symbol.iterator] 方法
3. ES2016(ES7)
- Array.prototype.includes
- 指数运算符( ** )
let a = [1,NaN,null,undefined];
a.indexOf(NaN);//-1
a.includes(NaN);//true
let b = 2**3; //8
let c = Math.pow(2,3); //8
4. ES2017(ES8)
- async / await
- String.prototype.{ padStart, padEnd }
- Object.{ values, entries }
- 函数参数列表结尾允许逗号
- Object.getOwnPropertyDescriptors
- 共享内存和原子操作:SharedArrayBuffer 和 Atomics
4.1 async / await
- 同步形式的代码处理异步操作
- 结合ES2018异步迭代的改进后,可以在循环中处理async await
for await (const line of readLines(filePath)) {
console.log(line);
}
4.2 String.prototype. { padStart, padEnd }
var str = 'hello';
str.padStart(4); // "hello"
str.padStart(8); // " hello"
str.padStart(8,'*'); // "***hello"
str.padStart(8,'12345'); // "123hello"
str.padStart(8, 'ab'); // "abahello"
str.padEnd(4); // "hello"
str.padEnd(8); // "hello "
str.padEnd(8,'*'); // "hello***"
str.padEnd(8,'12345'); // "hello123"
str.padEnd(8, 'ab'); // "helloaba"
4.3 Object.{ values, entries }
let val = { 'a':1, 'b':2, 'c':3 };
Object.values(val); // [1,2,3];
Object.entries(val); // [ ['a',1], ['b',2], ['c',1] ]
Object.entries(val).forEach( ([key,value])=>{
console.log(`key: ${key}, value: ${value}`);
});
// key: a, value: 1
// key: b, value: 2
// key: c, value: 3
4.4 函数参数列表结尾允许逗号
- 主要是为了改善git提交记录的可维护性
// 改进前
function testTrailingComma(
param1,
param2 //增加参数必须改变此行
){ ... }
// 改进后
function testTrailingComma(
param1,
param2, //增加参数不再影响此行
){ ... }
4.5 Object.getOwnPropertyDescriptors
let obj1 = { 'a': 1, test(){} };
console.log(Object.getOwnPropertyDescriptors(obj1));
/*{
a:{
configurable: true
enumerable: true
value: 1
writable: true
},
test:{
configurable: true
enumerable: true
value: ƒ test()
writable: true
}
}*/
4.6 共享内存和原子操作
- SharedArrayBuffer 和 Atomics
- SharedArrayBuffer: 共享二进制内存区域,以字节为单位
- Atomics:提供一组静态方法来对SharedArrayBuffer进行原子操作
5. ES2018(ES9)
- 异步迭代
- Promise.prototype.finally
- 对象rest/spread操作符
- 正则表达式改进:{ 命名捕获组, 反向断言,dotAll标记,Unicode转义 }
- 模版字符串改进: 支持非转义
5.1 Promise.prototype.finally
- 类比 try ... catch ... finally
Promise.resolve(2)
.then((val)=>{})
.catch((err)=>{})
.finally(()=>{})
5.2 对象rest / spread操作符
//rest
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x; // 1
y; // 2
z; // { a: 3, b: 4 }
//spread
let n = { x, y, ...z };
n; // { x: 1, y: 2, a: 3, b: 4 }
5.3 正则表达式改进-命名捕获组
//命名捕获组
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
let result = re.exec('2019-11-01');
// result.groups.year === '2019';
// result.groups.month === '11';
// result.groups.day === '01';
// result[0] === '2019-11-01';
// result[1] === '2019';
// result[2] === '11';
// result[3] === '01';
5.4 正则表达式改进-反向断言
let val = '$10.53';
//正向断言
let match = /(\$)(?=\d+(\.\d*))?/.exec(val); // match[0] -> $
//肯定反向断言
match = /(?<=\$)\d+(\.\d*)?/.exec(val); // match[0] -> 10.53
//否定反向断言
match = /(?<!\$)\d+(?:\.\d*)/.exec(val); // match[0] -> null
match = /(?<!\$)\d+(?:\.\d*)/.exec('¥10.53') // match[0] -> 10.53
5.5 正则表达式改进-dotAll标记
// 增加s标记,可以使 . 匹配 行结束符
//无标记
/foo.bar/.test('foo\nbar'); // false
//有标记
/foo.bar/s.test('foo\nbar'); // true
5.6 正则表达式改进-Unicode转义
- 支持
\p{...}
和\P{...}
实现Unicode转义 - 之前类似实现需要使用第三方库
const regexGreekSymbol = /\p{Script=Greek}/u;
regexGreekSymbol.test('π'); // true
5.7 模版字符串改进
- 解决
\u
,\x
,\数字
开头的转义字符被自动认定为unicode,16进制和8进制转义,导致某些特定字符被认定为非法的错误 - 比如
\unicode
,\xerxes
,\0100
都被认为是非法字符
6. ES2019 (ES10)
- 可选catch异常变量绑定
- 字符集扩充
- Symbol.prototype.description
- Object.fromEntries( )
- 完善JSON.stringify( )
- 改进Function.prototype.toString( )
- String.prototype.{ trimStart, trimEnd }
- Array.prototype.{ flat, flatMap }
6.1 可选catch异常变量绑定
- catch的error参数可省略
//修改前
try{} catch(err){} // ok
try{} catch{} // error
//修改后
try{} catch(err){} // ok
try{} catch{} // ok
6.2 字符集扩充
- 起因:JSON字符集 > ECMAScript字符集
- 增加2个字符:U+2028 LINE SEPARATOR , U+2029 PARAGRAPH SEPARATOR
6.3 Symbol.prototype.description
- 增加属性直接获取description
- 代替之前使用Symbol.prototype.toString方法间接获得
const aSymbol = Symbol('我是描述');
//以前
String(aSymbol); // Symbol('我是描述')
//现在
aSymbol.description;// '我是描述'
6.4 Object.fromEntries( )
- 和 Object.Entries( )相反
//嵌套数组转对象
obj = Object.fromEntries([['a', 0], ['b', 1]]); // { a: 0, b: 1 }
//map转对象
map = new Map([ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ]);
obj = Object.fromEntries(map); // {a: 1, b: 2, c: 3}
6.5 完善JSON.stringify( )
// 正确的unicode字符
JSON.stringify('𝌆');// → '"𝌆"'
JSON.stringify('\uD834\uDF06')// → '"𝌆"' -> 来自太玄经
// 超出字符集,返回转义的utf-8序列
JSON.stringify('\uDF06\uD834')// → '"\\udf06\\ud834"'
JSON.stringify('\uDEAD') // → '"\\udead"'
6.6 改进Function.prototype.toString( )
- 返回函数定义的全部内容,包括各种空格和注释
const a = () => { /*我是注释*/ return};
//以前
a.toString(); // '()=>{}'
//现在
a.toString(); // '() => { /*我是注释*/ return}'
6.7 String.prototype.{ trimStart, trimEnd}
var str = ' foo '; //3个空格+foo+2个空格
str.length; // 8
str.trimStart().length; // 5
str.trimStart(); // 'foo '
str.trimEnd().length; // 6
str.trimEnd(); // ' foo'
6.8 Array.prototype.{ flat, flatMap }
/*--------flat-----------*/
var arr1 = [1, 2, [3, 4, [5, 6]]];
arr1.flat(); // [1, 2, 3, 4, [5, 6]]
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(2); // [1, 2, 3, 4, 5, 6]
var arr3 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr3.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
/*--------flatMap-----------*/
let arr4 = [1, 2, 3, 4];
arr4.map(x => [x * 2]); // [[2], [4], [6], [8]]
arr4.flatMap(x => [x * 2]); // [2, 4, 6, 8]
arr4.flatMap(x => [[x * 2]]); // [[2], [4], [6], [8]]
7. ES2020 待发布(ES11)
- String.prototype.matchAll
- import( )
- 新增基本类型BigInt
- Promise.allSettled
- globalThis
7.1 String.prototype.matchAll
- 避免在循环中调用regexp.exec获取匹配项信息,分组捕获更方便
//以前
const regexp = /t(e)(st(\d?))/g;
const str = 'test1test2';
let match;
while ((match = regexp.exec(str)) !== null) {
console.log(`找到 ${match[0]} 开始位置=${match.index} 结束位置=${regexp.lastIndex}.`);
// 输出: "找到 test1 开始位置=0 结束位置=4."
// 输出: "找到 test2 开始位置=5 结束位置=9."
}
//现在
let matches = str.matchAll(regexp);
for(const match of matches){
console.log(`找到 ${match[0]} 开始位置=${match.index} 结束位置=${match.index+match[0].length}.`)
}//输出同上
[...str.matchAll(regexp)]; //['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4]
7.2 import( )
- 动态加载 import( )
- script tag中可用,不局限于模块中使用:例子
- 如果在模块中使用,只影响import之后的语句
- 接受字符串变量为参数,可以动态改变import的内容
- 不强制增加静态依赖关系
7.3 新增基本类型BigInt
- 现在有7种基本类型: number, undefined, null, string, boolean, symbol, bigInt
- 新类型要解决的问题
/*Number*/
const x = Number.MAX_SAFE_INTEGER; // 9007199254740991, 即2^53-1
const y = x + 1; // 9007199254740992
const z = x + 2 // 9007199254740992
- BigInt
/*BigInt*/
const theBiggestInt = 9007199254740991n;
0n+1; // TypeError: Cannot mix BigInt and other types, use explicit conversions
theBiggestInt + 2n; // 9007199254740993n;
5n/2n; // 2n
const alsoHuge = BigInt(9007199254740991); // 9007199254740991n
const hugeButString = BigInt('9007199254740991');// 9007199254740991n
0n === 0; //false
0n == 0; //true
Boolean(0n); // false
+1n; // TypeError: Cannot convert a BigInt value to a number
Number(1n);// 1
1n+ '2'; // '12'
7.4 Promise.allSettled
- 与 Promise.all 对应
- 所有Promise都有结果,reject / resolve 皆可
7.5 globalThis
- 全局对象受运行环境影响: 浏览器为window,node环境为global,其他环境可能为this
- 提供一个环境无关的方式访问全局对象