Welcome to Alex's Midway

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 字符集扩充

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
  • 提供一个环境无关的方式访问全局对象