JS语法 ES6、ES7、ES8、ES9、ES10、ES11、ES12新特性

  • 前言
  • ES6(2015)
  • 1. 类(class)
  • 2. 模块化(ES Module)
  • 3. 箭头函数
  • 4. 函数参数默认值
  • 5. 模板字符串(反引号``+${变量名})
  • 6. [解构赋值](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)(注意范例里边的重点注意事项)
  • 7. 展开运算符(延展操作符)
  • 8. 对象属性简写
  • 9. Promise
  • 10. let 和 const
  • 面试题:let 和 const、var 的区别:
  • 11. set 和 map
  • set
  • map
  • ES7(2016)
  • 1. Array.prototype.includes()
  • 2. 指数操作符
  • ES8(2017)
  • 1. async/await(异步终极解决方案)
  • 2. Object.keys(obj) Object.values(obj) Object.entries(obj)
  • 3. String padding
  • - String.prototype.padStart()
  • 1、`targetLength` :当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。
  • 2、`padString`:填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。此参数的默认值为 " "(U+0020)。
  • - String.prototype.padEnd()
  • 4. 函数参数列表结尾允许逗号
  • 5. Object.getOwnPropertyDescriptors(obj)
  • 6. SharedArrayBuffer对象
  • 7. [Atomics对象]()
  • ES9(2018)
  • 1. 异步迭代
  • 2. Promise.finally()
  • 3. Rest/Spread 属性(展开运算符...和剩余运算符...)
  • 4. 正则表达式命名捕获组
  • 5. 正则表达式反向断言
  • 6. 正则表达式dotAll模式
  • 7. 正则表达式 Unicode 转义
  • 8. 非转义序列的模板字符串
  • ES10新特性(2019)
  • 1. 行分隔符(U + 2028)和段分隔符(U + 2029)符号现在允许在字符串文字中,与JSON匹配
  • 2. 更加友好的 JSON.stringify
  • 3. 新增了Array的flat()方法和flatMap()方法
  • 4. 新增了String的`trimStart()`方法和`trimEnd()`方法
  • 5. Object.fromEntries()
  • 6. Symbol.prototype.description
  • 7. String.prototype.matchAll
  • 8. `Function.prototype.toString()`现在返回精确字符,包括空格和注释
  • 9. 修改 catch 绑定
  • 10. 新的基本数据类型BigInt


前言

本文集合了 ES6 至 ES11 常用到的特性,包括还在规划的 ES12,只列举大概使用,详细介绍的话内容量将十分巨大.。PS:使用新特性需要使用最新版的 bable 就行转义。

ES6(2015)

  • 模块化
  • 箭头函数
  • 函数参数默认值
  • 模板字符串
  • 解构赋值
  • 延展操作符
  • 对象属性简写
  • Promise
  • Let与Const

1. 类(class)

class Man {
  constructor(name) {
    this.name = '赵十三';
  }
  console() {
    console.log(this.name);
  }
}
const man = new Man('赵十三');
man.console(); // 赵十三

2. 模块化(ES Module)

// 模块 A 导出一个方法
export const sub = (a, b) => a + b;
// 模块 B 导入使用
import { sub } from './A';
console.log(sub(1, 2)); // 3

3. 箭头函数

const func = (a,b) => console.log(a + b);
func(1,2) // 3

4. 函数参数默认值

function func(a = 11,){
	// 调用func函数未传参数时,控制台默认打印 11
	// 调用func函数传了参数时,控制台打印传的参数
	console.log(a)
}

5. 模板字符串(反引号``+${变量名})

const name = '赵十三'
const str = `我的名字是${name}`
console.log(str) // 我的名字是赵十三

6. 解构赋值(注意范例里边的重点注意事项)

var a, b, rest;
[a, b] = [10, 20];
console.log(a); // 10
console.log(b); // 20
// 将剩余的值赋值给一个变量
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]
// 结构对象
({ a, b } = { a: 10, b: 20 });
console.log(a); // 10
console.log(b); // 20
// Stage 4(已完成)提案中的特性
// 结构对象 + 将剩余对象赋值给一个变量
// 注意:1、赋值语句周围的圆括号 ( ... ) 在使用对象字面量无声明解构赋值时是必须的!!!!!!一定要注意!!!!!!
// 注意:2、( ... ) 表达式之前需要有一个分号,否则它可能会被当成上一行中的函数执行!!!!!!!!
({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40});
console.log(a); // 10
console.log(b); // 20
console.log(rest); // {c: 30, d: 40}
// 交换变量
a = 1;b = 3;
[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1

7. 展开运算符(延展操作符)

let a = [...'hello world']; // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d"]

8. 对象属性简写

const name='小豪',
const obj = { name };

9. Promise

Promise.resolve().then(() => { console.log(2); });
console.log(1);
// 先打印 1 ,再打印 2

10. let 和 const

let name = '小豪';
const arr = [];
面试题:let 和 const、var 的区别:
  • let : 定义变量
let 和 var 的区别:
 * 1.var可以重复定义,let不可以重复定义
 * 2. var有变量提升,let没有变量提升 (也可以叫预解析)
 * 3. var没有块级作用域,let有块级作用域
  • const:定义常量,即值不会改变的量
注意:**const定义常量时必须赋值**
那么,如何改变const定义的值呢???
答:**可以通过给const定义一个对象,那对象中的属性是可以改变值的 **
例如:const obj = {name: 'alice', age:20}
      obj.name='jack'

11. set 和 map

set
set :可以理解成是不重复的数组
add:添加一个值
delete:删除一个值
clear:删除所有值
通过for of 来遍历set数据结构
面试题:1、如何通过set可实现数组去重?
  var arr=[3,4,5,2,2,3,5,6,3,5,65,46,34,23,2]
  Array.from(new Set(arr))或[...new Set(arr)]
面试题:2、set 和 数组 的互转
  ** 数组转换成set:new Set(数组)
  ** set转换成数组:Array.from(set类型的变量)
map
map:可以理解成是一个对象
注意:注意这里的map是一个构造函数,不是数组中的map
优点:可以将将任意的数据类型的值当作对象的属性来使用,避免了传统的对象属性只有是字符串类型

ES7(2016)

1. Array.prototype.includes()

[1].includes(1); // true

2. 指数操作符

2**10; // 1024

ES8(2017)

  • async/await
  • Object.values()
  • Object.entries()
  • String padding:padStart()和padEnd(),填充字符串达到当前长度
  • 函数参数列表结尾允许逗号
  • Object.getOwnPropertyDescriptors()
  • ShareArrayBuffer和Atomics对象,用于从共享内存位置读取和写入

1. async/await(异步终极解决方案)

async getData(){
    const res = await api.getTableData(); // await 异步任务
    // do something    
}

2. Object.keys(obj) Object.values(obj) Object.entries(obj)

Object.keys(obj) —— 返回一个包含该对象所有的键的数组。
Object.values(obj) —— 返回一个包含该对象所有的值的数组。
Object.entries(obj) —— 返回一个包含该对象所有 [key, value] 键值对的数组。

Object.keys({a: 1, b: 2, c: 3}); //  ['a', 'b', 'c']
Object.values({a: 1, b: 2, c: 3}); // [1, 2, 3]
Object.entries({a: 1, b: 2, c: 3}); // [["a", 1], ["b", 2], ["c", 3]]

3. String padding

- String.prototype.padStart()

语法:str.padStart(targetLength [, padString]) 参数:

1、targetLength :当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。
2、padString:填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。此参数的默认值为 " "(U+0020)。

返回值:在原字符串开头填充指定的填充字符串直到目标长度所形成的新字符串。

const fullNumber = '2034399002125581';
const last4Digits = fullNumber.slice(-4);
const maskedNumber = last4Digits.padStart(fullNumber.length, '*');
console.log(maskedNumber); // ************5581
- String.prototype.padEnd()

padEnd()区别于padStart() 的就是它是从右侧开始填充

4. 函数参数列表结尾允许逗号

5. Object.getOwnPropertyDescriptors(obj)

用来获取一个对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象

6. SharedArrayBuffer对象

SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区

// 语法:new SharedArrayBuffer(length)
// 参数:length指所创建的数组缓冲区的大小,以字节(byte)为单位。
// 返回值:一个大小指定的新 SharedArrayBuffer 对象。其内容被初始化为 0。
// 注意:需要new运算符构造
// 创建一个1024字节的缓冲
let sab = new SharedArrayBuffer(1024);

7. Atomics对象

Atomics 对象提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作。
这些原子操作属于 Atomics 模块。与一般的全局对象不同,Atomics 不是构造函数,因此不能使用 new 操作符调用,也不能将其当作函数直接调用。Atomics 的所有属性和方法都是静态的(与 Math 对象一样)。
多个共享内存的线程能够同时读写同一位置上的数据。原子操作会确保正在读或写的数据的值是符合预期的,即下一个原子操作一定会在上一个原子操作结束后才会开始,其操作过程不会中断。
具体相关内容可以看看链接里的讲解,有很多!!!

ES9(2018)

  • 异步迭代
  • Promise.finally()
  • Rest/Spread 属性
  • 正则表达式命名捕获组(Regular ExpressionNamed Capture Groups)
  • 正则表达式反向断言(lookbehind)
  • 正则表达式dotAll模式
  • 正则表达式Unicode 转义
  • 非转义序列的模板字符串

1. 异步迭代

await可以和for…of循环一起使用,以串行的方式运行异步操作

async function process(array) {
  for await (let i of array) {
    // doSomething(i);
  }
}

2. Promise.finally()

Promise.resolve().then().catch(e => e).finally();

3. Rest/Spread 属性(展开运算符…和剩余运算符…)

  • 用于对象解构的 rest 操作符(…)。目前,这个操作符只能在数组解构和参数定义中使用
  • 对象字面量中的 spread 操作符(…)。目前,这个操作符只能用于数组字面量和在函数方法中调用。
// 展开运算符(...)
const values = [1, 2, 3, 5, 6];
console.log( Math.max(...values) ); // 6
// 剩余运算符(...)
const style = {
  width: 300,
  marginLeft: 10,
  marginRight: 30
};
const { width, ...margin } = style;
console.log(width);  // => 300
console.log(margin); // => { marginLeft: 10, marginRight: 30 }

4. 正则表达式命名捕获组

JavaScript正则表达式可以返回一个匹配的对象——一个包含匹配字符串的类数组,例如:以YYYY-MM-DD的格式解析日期:

const
  reDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/,
  match  = reDate.exec('2018-04-30'),
  year   = match[1], // 2018
  month  = match[2], // 04
  day    = match[3]; // 30

这样的代码很难读懂,并且改变正则表达式的结构有可能改变匹配对象的索引。
ES2018允许命名捕获组使用符号?<name>,在打开捕获括号(后立即命名,示例如下:

const
  reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
  match  = reDate.exec('2018-04-30'),
  year   = match.groups.year,  // 2018
  month  = match.groups.month, // 04
  day    = match.groups.day;   // 30

任何匹配失败的命名组都将返回undefined
命名捕获也可以使用在replace()方法中。例如将日期转换为美国的 MM-DD-YYYY 格式

const
  reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
  d      = '2018-04-30',
  usDate = d.replace(reDate, '$<month>-$<day>-$<year>');

5. 正则表达式反向断言

目前JavaScript在正则表达式中支持先行断言(lookahead)。这意味着匹配会发生,但不会有任何捕获,并且断言没有包含在整个匹配字段中。例如从价格中捕获货币符号:

const
  reLookahead = /\D(?=\d+)/,
  match       = reLookahead.exec('$123.89');
console.log( match[0] ); // $

ES2018引入以相同方式工作但是匹配前面的反向断言(lookbehind),这样我就可以忽略货币符号,单纯的捕获价格的数字:

const
  reLookbehind = /(?<=\D)\d+/,
  match        = reLookbehind.exec('$123.89');

console.log( match[0] ); // 123.89

以上是 肯定反向断言,非数字\D必须存在。同样的,还存在 否定反向断言,表示一个值必须不存在,例如:

const
  reLookbehindNeg = /(?<!\D)\d+/,
  match           = reLookbehind.exec('$123.89');

console.log( match[0] ); // null

6. 正则表达式dotAll模式

正则表达式中点.匹配除回车外的任何单字符,标记s改变这种行为,允许行终止符的出现,例如:

/hello.world/.test('hello\nworld');  // false
/hello.world/s.test('hello\nworld'); // true

7. 正则表达式 Unicode 转义

到目前为止,在正则表达式中本地访问 Unicode 字符属性是不被允许的。ES2018添加了 Unicode 属性转义——形式为\p{...}\P{...},在正则表达式中使用标记 u (unicode) 设置,在\p块儿内,可以以键值对的方式设置需要匹配的属性而非具体内容。例如:

const reGreekSymbol = /\p{Script=Greek}/u;
reGreekSymbol.test('π'); // true

此特性可以避免使用特定 Unicode 区间来进行内容类型判断,提升可读性和可维护性。

8. 非转义序列的模板字符串

之前,\u开始一个 unicode 转义,\x开始一个十六进制转义,\后跟一个数字开始一个八进制转义。这使得创建特定的字符串变得不可能,例如Windows文件路径 C:\uuu\xxx\111。更多细节参考模板字符串。

ES10新特性(2019)

  • 行分隔符(U + 2028)和段分隔符(U + 2029)符号现在允许在字符串文字中,与JSON匹配
  • 更加友好的 JSON.stringify
  • 新增了Array的flat()方法和flatMap()方法
  • 新增了String的trimStart()方法和trimEnd()方法
  • Object.fromEntries()
  • Symbol.prototype.description
  • String.prototype.matchAll
  • Function.prototype.toString()现在返回精确字符,包括空格和注释
  • 简化try {} catch {},修改 catch 绑定
  • 新的基本数据类型BigInt
  • globalThis
  • import()
  • Legacy RegEx
  • 私有的实例方法和访问器

1. 行分隔符(U + 2028)和段分隔符(U + 2029)符号现在允许在字符串文字中,与JSON匹配

以前,这些符号在字符串文字中被视为行终止符,因此使用它们会导致SyntaxError异常。

2. 更加友好的 JSON.stringify

如果输入 Unicode 格式但是超出范围的字符,在原先JSON.stringify返回格式错误的Unicode字符串。现在实现了一个改变JSON.stringify的第3阶段提案,因此它为其输出转义序列,使其成为有效Unicode(并以UTF-8表示)

3. 新增了Array的flat()方法和flatMap()方法

数组扁平化概念:如何将多维数组转换成一维数组的过程
flat()flatMap()本质上就是是归纳(reduce) 与 合并(concat)的操作。
Array.prototype.flat()
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

  • flat()方法最基本的作用就是数组降维
var arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

//使用 Infinity 作为深度,展开任意深度的嵌套数组
arr3.flat(Infinity); 
// [1, 2, 3, 4, 5, 6]
  • 其次,还可以利用flat()方法的特性来去除数组的空项
var arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]

Array.prototype.flatMap()
flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。
这里我们拿map方法与flatMap方法做一个比较。

var arr1 = [1, 2, 3, 4];

arr1.map(x => [x * 2]); 
// [[2], [4], [6], [8]]

arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]

// 只会将 flatMap 中的函数返回的数组 “压平” 一层
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]

4. 新增了String的trimStart()方法和trimEnd()方法

新增的这两个方法很好理解,分别去除字符串首尾空白字符,这里就不用例子说声明了。

5. Object.fromEntries()

Object.entries()方法的作用是返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。
Object.fromEntries() 则是 Object.entries()反转
Object.fromEntries() 函数传入一个键值对的列表,并返回一个带有这些键值对的新对象。这个迭代参数应该是一个能够实现@iterator方法的的对象,返回一个迭代器对象。它生成一个具有两个元素的类似数组的对象,第一个元素是将用作属性键的值,第二个元素是与该属性键关联的值。

  • 通过 Object.fromEntries, 可以将 Map 转化为 Object:
const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 42 }
  • 通过 Object.fromEntries, 可以将 Array 转化为 Object:
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
const obj = Object.fromEntries(arr);
console.log(obj); // { 0: "a", 1: "b", 2: "c" }

6. Symbol.prototype.description

通过工厂函数Symbol()创建符号时,您可以选择通过参数提供字符串作为描述:

const sym = Symbol('The description');

以前,访问描述的唯一方法是将符号转换为字符串:

assert.equal(String(sym), 'Symbol(The description)');

现在引入了getter Symbol.prototype.description以直接访问描述:

assert.equal(sym.description, 'The description');

7. String.prototype.matchAll

matchAll() 方法返回一个包含所有匹配正则表达式及分组捕获结果的迭代器。
在 matchAll 出现之前,通过在循环中调用regexp.exec来获取所有匹配项信息(regexp需使用/g标志:

const regexp = RegExp('foo*','g');
const str = 'table football, foosball';

while ((matches = regexp.exec(str)) !== null) {
  console.log(`Found ${matches[0]}. Next starts at ${regexp.lastIndex}.`);
  // expected output: "Found foo. Next starts at 9."
  // expected output: "Found foo. Next starts at 19."
}

如果使用matchAll ,就可以不必使用while循环加exec方式(且正则表达式需使用/g标志)。使用matchAll 会得到一个迭代器的返回值,配合 for…of, array spread, or Array.from() 可以更方便实现功能:

const regexp = RegExp('foo*','g'); 
const str = 'table football, foosball';
let matches = str.matchAll(regexp);

for (const match of matches) {
  console.log(match);
}
// Array [ "foo" ]
// Array [ "foo" ]

// matches iterator is exhausted after the for..of iteration
// Call matchAll again to create a new iterator
matches = str.matchAll(regexp);

Array.from(matches, m => m[0]);
// Array [ "foo", "foo" ]

matchAll可以更好的用于分组

var regexp = /t(e)(st(\d?))/g;
var str = 'test1test2';

str.match(regexp); 
// Array ['test1', 'test2']
let array = [...str.matchAll(regexp)];

array[0];
// ['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4]
array[1];
// ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4]

8. Function.prototype.toString()现在返回精确字符,包括空格和注释

function /* comment */ foo /* another comment */() {}

// 之前不会打印注释部分
console.log(foo.toString()); // function foo(){}

// ES2019 会把注释一同打印
console.log(foo.toString()); // function /* comment */ foo /* another comment */ (){}

// 箭头函数
const bar /* comment */ = /* another comment */ () => {};

console.log(bar.toString()); // () => {}

9. 修改 catch 绑定

在 ES10 之前,我们必须通过语法为 catch 子句绑定异常变量,无论是否有必要。很多时候 catch 块是多余的。 ES10 提案使我们能够简单的把变量省略掉。
不算大的改动。
之前是try {} catch(e) {}现在是try {} catch {}

10. 新的基本数据类型BigInt

现在的基本数据类型(值类型)不止5种(ES6之后是六种)了哦!加上BigInt一共有七种基本数据类型,分别是:String、Number、Boolean、Null、Undefined、Symbol、BigInt