Javascript[ECMAScript] ES6、ES7、ES8、ES9、ES10、ES11、ES12

前言

鉴于找不到ES6-ES14 的新特性集合,所以有了这篇文章,后续会持续更新每年的新特性

背景

JS1.1(1997)

第一版基于Netscape Navigator 3.0中实现的JAVASCRIPT 1.1

JS1.2(1999)

基于Netscape Navigator 4.0中实现的JavaScript 1.2。添加了正则表达式、更好的字符串处理、新的控制语句、Try/Catch异常处理、更严格的错误定义、数字输出格式以及其他增强功能

ES5(2015)

1.严格模式

js 复制代码
use strict

2.Object

  • getPrototypeOf,返回一个对象的原型
  • getOwnPropertyDescriptor,返回某个对象自有属性的属性描述符
  • getOwnPropertyNames,返回一个数组,包括对象所有自由属性名称集合(包括不可枚举的属性)
  • hasOwnProperty,给定属性存在于对象实例中,返回 true
  • isPrototypeOf,在原型链中出现过的原型,返回 true
  • create,创建一个拥有指定原型和若干指定属性的对象
  • defineProperty,为对象定义一个新属性,或者修改已有的属性,并对属性重新设置 getter 和 setter,这里可以被用作数据绑定的对象劫持用途
  • defineProperties,在一个对象上添加或修改一个或者多个自有属性,与 defineProperty 类似
  • seal,锁定对象,阻止修改现有属性的特性,并阻止添加新属性,但是可以修改已有属性的值
  • freeze,冻结对象,阻止对对象的一切操作和更改,冻结对象将变成只读
  • preventExtensions,让一个对象变得不可以扩展,也就是不能再添加新的属性
  • isSealed,判断对象是否被锁定
  • isFrozen,判断对象是否被冻结
  • isExtensible,判断对象是否可以被扩展
  • keys,返回一个由给定对象的所有可以枚举自身属性的属性名组成的数组

3.Array

  • Array.isArray,确定某个值到底是不是数组,不管它在哪个全局执行环境中创建
  • push,接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度
  • pop,在数组尾部移除最后一项,减少数组的 length 值,然后返回移除的项
  • shift,移除数组中的第一项并返回该项,同时将数组长度减 1
  • unshift,在数组前端添加任意个项,并返回新数组的长度
  • reverse,反转数组项的顺序,返回排序后的数组
  • sort,默认情况下,按升序排序数组项,返回排序后的数组
  • concat,创建当前数组的一个副本,将接收到的参数添加到这个副本的末尾,返回新构建的数组
  • join,用指定的字符拼接数组,返回拼接好的字符串
  • slice,基于当前数组中的一个或多个创建一个新数组,不影响原始数组
  • splice,根据传入参数不同,可以删除、插入、替换数组元素,返回一个数组,该数组中包含从原始数组中删除的项,如果没有删除任何项,则返回空数组
  • indexOf,返回根据给定元素找到的第一个索引值,如果不存在则返回 -1
  • lastIndexOf,返回指定元素在数组中的最后一个索引值,如果不存在则返回 -1
  • every ,对数组中的每一项运行给定函数 ,如果该函数对每一项都返回 true,则返回 true
  • filter,对数组中的每一项运行给定函数 ,返回该函数会返回 true 的项组成的数组
  • forEach,对数组中的每一项运行给定函数,没有返回值
  • map,对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组
  • some,对数组中的每一项运行给定函数,如果函数对任一项返回 true,则返回 truereduce,接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终缩减为一个值
  • reduceRight,接收一个函数作为累加器,数组中的每个值(从右到左)开始缩减,最终缩减为一个值

4.String

  • charAt,访问字符串中特定字符,返回给定位置的字符
  • charCodeAt,访问字符串中特定字符,返回给定位置的字符的字符编码
  • concat,将一个或多个字符串拼接起来,返回拼接得到的新字符串
  • match,查找匹配的字符串,返回一个数组
  • search,查找匹配的字符串,返回匹配项的索引,没有找到,返回 -1
  • replace,替换字符串
  • split,基于指定的分隔符将一个字符串分割成多个字符串,将结果放在一个数组中,并返回
  • trim,创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果
  • localeCompare,比较两个字符串
  • slice,返回被操作字符串的一个子字符串
  • substr,返回被操作字符串的一个子字符串
  • substring,返回被操作字符串的一个子字符串

注意:slice、substr、substring 都接受一个或两个参数,第一个参数指定字符串的开始位置,第二个参数表示子字符串到哪里结束,slice 和 substring 的第二个参数指定的是子字符串的最后一个字符后面的位置,substr 第二个参数指定的是返回的字符个数,如果没有传递第二个参数,则将字符串的末尾作为结束位置。

5.Function

  • arguments.callee,该属性是一个指针,指向拥有这个 arguments 对象的函数
  • arguments.caller,该属性保存着调用当前函数的函数的引用
  • apply,在特定的作用域中调用函数,第一个参数是在其中运行函数的作用域,第二个是参数数组
  • call,在特定的作用域中调用函数,第一个参数是在其中运行函数的作用域,其余参数直接传递给函数
  • bind,创建一个函数的实例,其 this 值会被绑定到传给 bind 函数的值

6.JSON

  • JSON.parse,解析字符串为 JSON 对象
  • JSON.stringify,解析 JSON 对象为字符串
  • JSON.valueOf,获取某个JSON 对象中的值
  • JSON.toString,被调用时,会调用 Object 原型上的 toString 方法,会获的 JSON 对象的值并转为字符串,如果没有具体的值,则返回原型数组
  • JSON.toLocaleString,也是Object 原型上的方法,经常会返回与 toString 相同内容,但是对于 Date 对象,则会返回格式化的时间字符串

7.其他

  • Date.now,返回当前时间戳
  • Date.toJSON,将 Date 对象转换为字符串,格式为 JSON 日期
  • Math.min,确定一组数值中的最小值
  • Math.max,确定一组数值中的最大值
  • Math.random,返回大于 0 小于 1 的一个随机数
  • Number.prototype.toFixed,按照指定的小数位返回数值的字符串表示

ES6(2015)

1. 类(class)

js 复制代码
class Man {
  constructor(name) {
    this.name = '你好世界';
  }
  console() {
    console.log(this.name);
  }
}
const man = new Man('你好世界');
man.console(); // 你好世界

2. 模块化(ES Module)

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

3. 箭头(Arrow)函数

js 复制代码
const func = (a, b) => a + b;
func(1, 2); // 3

4. 函数参数默认值

复制代码
function foo(age = 25,){ // ...}

5. 模板字符串

js 复制代码
const name = '你好世界';
const str = `Your name is ${name}`;

6. 解构赋值

js 复制代码
let a = 1, b= 2;
[a, b] = [b, a]; // a 2  b 1

7. 延展操作符

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

8. 对象属性简写

js 复制代码
const name='你好世界',
const obj = { name };

9. Promise

Promise 代表一个异步操作的执行返回状态,这个执行返回状态在 Promise 对象创建时是未知的,它允许为异步操作的成功或失败指定处理方法。

9.1 Promise 的状态有三种:
  • Fulfilled,表示 Promise 执行成功
  • Rejected,表示 Promise 执行失败
  • Pending,表示 Promise 正在执行中
9.2 Promise 对象有两个特点:
  • 对象的状态不受外界影响
  • 一旦状态改变就不会再变,任何时候都可以得到这个结果
9.3 缺点:
  • 无法取消 Promise,一旦新建它就会立即执行,无法中途取消
  • 如果不设置回调函数,Promise 内部抛出的错误不会反应到外部
  • 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚开始还是即将完成)
9.4 基本用法:
js 复制代码
var promise = new Promise(function(resolve, reject) {
    // ... some code

    if ( /*异步操作成功*/ ) {
        resolve(value)
    } else {
        reject(error)
    }
})

promise.then(function(value) {
    // success
}, function(error) {
    // failure
})
9.5 Promise 实例方法:
  • Promise.prototype.then(),为 Promise 实例添加状态改变时的回调函数,返回一个新的 Promise 实例
  • Promise.prototype.catch(),用于指定发生错误时的回调函数,返回一个新的 Promise 实例
  • Promise.prototype.done(),总是处于回调链的尾端,保证捕捉到任何可能出现的错误
  • Promise.prototype.finally(),用于指定不管 Promise 对象最后状态如何都会执行的操作。

finally 与 done 的最大区别在于:finally 接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。

9.6 Promise 对象方法:
  • Promise.all(),将多个 Promise 实例包装成一个新的 Promise 实例。
js 复制代码
var p = Promise.all([p1, p2, p3])

p 的状态由 p1,p2,p3 决定:

第一,只有 p1,p2,p3 的状态都编程 Fulfilled,p 的状态才会变成 Fulfilled,此时p1,p2,p3 的返回值组成一个数组,传递给 p 的回调函数。

第二,只要 p1,p2,p3 中有一个被 Rejected,p 的状态就变成 Rejected,此时第一个被 Rejected 的实例的返回值会传递给 p 的回调函数

  • Promise.race(),将多个 Promise 实例包装成一个新的 Promise 实例。
js 复制代码
var p = Promise.race([p1, p2, p3])

只要 p1,p2,p3 中有一个实例率先改变状态,p 的状态就跟着改变,那个率先改变的 Promise 实例的返回值就传递给 p 的回调函数。

  • Promise.resolve(),将现有对象转为 Promise 对象,状态为 Resolved
  • Promise.reject(),将现有对象转为 Promise 对象,状态为 Rejected
js 复制代码
Promise.resolve().then(() => { console.log(2); });
console.log(1);
// 先打印 1 ,再打印 2

10. let和const

js 复制代码
let name = '你好世界';
const arr = [];

11. 数组的新特性

ES6 为数组内置对象添加了较多的新特性,主要包括 ... 复制数组和新增的数组 API。

复制代码
const arr = [1, 2, 3]
const newArr = [...arr]  //[1, 2, 3]

注意:这里 ... 进行的数组复制是浅拷贝

ES6 数组的新增方法:

  • Array.from,用于将类数组对象(包括 [array-like object] 和可遍历对象)转化为真正的数组
  • Array.of,可以将传入的一组参数值转换为数组
  • Array.prototype.copyWithin,可以在当前数组内部将指定位置的数组项复制到其他位置,然后返回当前数组,使用 copyWithin 方法会修改当前数组
  • Array.prototype.fill,使用给定值,填充一个数组,会改变原来的数组
  • Array.prototype.find,用于找出第一个符合条件的数组元素,有点类似于 filter
  • Array.prototype.findIndex,用来返回某个特定数组元素在数组中的位置
  • Array.prototype.entries,对数组中键值对进行遍历
  • Array.prototype.keys,对数组键名进行遍历
  • Array.prototype.values,对数组键值进行遍历
  • for...of 循环进行遍历
  • Array.prototype[Symbol.iterator],用来获取遍历数组对象的迭代器

11. 函数参数

ES6 对函数参数进行了新的设计,主要添加了默认参数、不定参数和扩展参数:

js 复制代码
// 默认参数
function sayHi(name = 'zhangsan') {
    console.log(`Hello ${name}`)
}
sayHi() // Hello zhangsan


// 不定参数
function sayHi(...name) {
    console.log(name.reduce((a, b) => `Hello ${a} ${b}`))
}
sayHi('zhangsan', 'lisi') 


// 扩展参数
let name = ['zhangsan', 'lisi']
function sayHi(name1, name2) {
    console.log(`Hello ${name1} ${name2}`)
}
sayHi(...name) 

不定参数和扩展参数可以认为恰好是相反的两个模式,不定参数是使用数组来表示多个参数,扩展参数则是将多个参数映射到一个数组。

需要注意:不定参数的 ... 和数组复制的 ... 是有区别的,不定参数可以使用函数的形参来表示所有的参数组成的列表。以前的 arguments 变量也有类似的作用,但是 arguments 不是真正的数组,除了存放参数的列表外,arguments 还有 length 属性,严格来说 arguments 是一个类数组对象,而不定参数则是一个完全的数组,这也是不定参数相对于 arguments 的优势,更加方便我们使用,所以建议使用不定参数来代替 arguments。

12. 集合类型 Map + Set + WeakMap + WeakSet

12.1 Set

Set 本身是一个构造函数 ,用来生成 Set 数据结构,Set 类似于数组(但它不是数组),Set 的成员的值都是唯一的,没有重复的值,也常用它来去重(不可以传递对象)。像 Set 加入值的时候,不会发生类型转换,所以 5 和 "5" 是两个不同的值。

复制代码
const arr = new Set([1, 2, 3, 4, 5, 5, 5, 5])

console.log(arr)  //[1, 2, 3, 4, 5]
console.log(arr.size)  //5 

操作方法:

  • add(value),添加某个值,返回 Set 结构本身
  • delete value,删除某个值,返回一个布尔值,表示删除是否成功
  • has(value),返回一个布尔值,表示该值是否为 Set 成员
  • clear(),清除所有成员,没有返回值

遍历方法:

  • keys(),返回键名的遍历器
  • values(),返回键值的遍历器
  • entries(),返回键值对的遍历器
  • forEach(),使用回调函数遍历每个成员
12.2 WeakSet

WeakSet 结构与 Set 类似,也是不重复的值的集合,但是,它与 Set 有两个区别:

第一,WeakSet 的成员只能是对象,而不能是其他类型的值。

第二,WeakSet 中的对象都是弱引用,即垃圾回收机制 不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不在引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象是否还存在于 WeakSet 中。因此 ES6 规定 WeakSet 不可遍历。

12.3 Map

传统的 JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是只能用字符串当做键,这给它的使用带来了很大的限制,而 ES6 提供了 Map 数据结构,它类似于对象,也是键值对的集合,但是"键" 的范围不限于字符串,各种类型的值(包括对象)都可以当做键。也就是说,Object 结构提供了"字符串-值"的对应,Map 结构提供了"值-值"的对应,是一种更完善的 Hash 结构实现。Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。

操作方法:

  • set(key, value),设置 key 所对应的键值,返回整个 Map 结构 ,如果 key 已经有值,则键值会被更新,否则就生成该键
  • get(key),读取 key 对应的键值,如果在好不到 key,则返回 undefined
  • has(key),返回一个 布尔值,表示某个键是否在 Map 数据结构中
  • delete(key),删除某个键,返回 true,如果删除失败,则返回 false
  • clear(),清除所有成员,没有返回值

遍历方法:

  • keys(),返回键名的遍历器
  • values(),返回键值的遍历器
  • entries(),返回所有成员的遍历器
  • forEach(),遍历 Map 的所有成员
12.4 WeakMap

WeakMap 结构与 Map 结构类似,也用于生成键值对的集合,但 WeakMap 与 Map 有两个个区别:

第一,WeakMap 只接受对象作为键名(null 除外),不接受其他类型的值作为键名。

第二,WeakMap 的键名所指向的对象不计入垃圾回收机制。它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内,因此,只要所引用的对象的其他引用被清除了,垃圾回收机制就会释放该对象所占用的内存。也就是说, 一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。基本上,如果要想对象中添加数据又不想干扰垃圾回收机制,便可以使用 WeakMap。一个典型的应用场景是,在网页的 DOM 元素上添加数据时就可以使用 WeakMap 结构,当该 DOM 元素被清除,其对应的 WeakMap 记录就会自动被移除。

注意:WeakMap 的专用场景就是它的键所对应的对象可能会在将来消失,WeakMap 结构有助于防止内存泄露。但是,WeakMap 弱引用的只是键名而不是键值,键值依然是正常引用的。

13. Symbol 类型

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值,它是 JavaScript 语言的第 7 种数据类型,前 6 种分别是:Undefined、Null、Boolean、String、Number 和 Object。

Symbol 值通过 Symbol 函数生成,一般作为属性键值,并且能避免对象属性键的命名冲突。也就是说,对象的属性名现在可以有两种类型:一种是原来就有的字符串,另一种就是新增的 Symbol 类型。只要属性名属于 Symbol 类型,就是独一无二的,可以保证不会与其他属性名产生冲突。

js 复制代码
let s = Symbol('foo')

typeof s  //"symbol"

注意:

Symbol 函数前不能使用 new 命令,否则会报错,这是因为生产的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

Symbol 函数的参数只表示对当前 Symbol 值的描述,因此相同参数的 Symbol 函数的返回值是不相等的。

Symbol 值作为对象属性名时不能使用点运算符:

js 复制代码
let s = Symbol()

let obj = {
    [s]: function() {
        console.log('Hello')
    }
}

obj[s]()  // 'Hello'

Symbol 做为属性名,该属性不会出现在 for...in,for...of 循环中,也不会被 Object.keys()、Object.getOwnpropertyNames() 返回,但它也不是私有属性,Object.getOwnPropertySymbols() 可以获取指定对象的所有 Symbol 属性名。 Object.getOwnPropertySymbols() 返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

Proxy

Proxy 用于修改某些操作的默认行为,可以用来拦截某个对象的属性访问方法,然后重载对象的 " . " 运算符。

Proxy 可以理解成在目标对象前架设一个"拦截"层,外界对该对象的访问都必须先通过这层拦截,因此提供了一种机制可以对外界的访问进行过滤和改写。

js 复制代码
let object = new Proxy({}, {
    get: function(target, key, receiver) {
        console.log(`getting ${key}`)
        return Reflect.get(target, key, receiver)
    },
    set: function(target, key, value, receiver) {
        console.log(`setting ${key}`)
        return Reflect.set(target, key, value, receiver)
    }
})

对比 Object.defineProperty 方法:

js 复制代码
let object = {},
    value

Object.defineProperty(object, 'value', {
    get: function() {
        console.log('getting value')
        return value
    },
    set: function(newValue) {
        value = newValue
        console.log('setting: ' + newValue)
    },
    enumerable: true,
    configurable: true
})

Reflect

Reflect 对象的设计目的有以下几个:

第一,将 Object 对象的一些明显属于语言内部的方法(如 Object.defineProperty)放到 Reflect 对象上,现阶段,某些方法同时在 Object 和 Reflect 对象上部署,未来新的方法只在 Reflect 对象上部署。也就是说,从 Reflect 对象上可以获得语言内部的方法。

第二,修改某些 Object 方法的返回结果,让其变得更加合理。

第三,让 Object 操作都编程函数行为,某些 Object 操作是命令式,比如 name in obj 和 delete obj [name],而 Reflect.has(obj, name) 和 Reflect.deleteProperty(obj, name) 让它们变成了函数行为。

第四,Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法,这就是 Proxy 对象可以方便的调用对应的 Reflect 方法来完成默认行为,作为修改行为的基础。也就是说,无论 Proxy 怎么修改默认行为,我们总可以在 Reflect 上获取到默认行为。

Iterator

Iterator即迭代器,它是一种接口,为各种不同的数据结构提供了统一的访问机制,换句话说,只要有任何数据结构部署了迭代接口,就可以使用统一的方式的来遍历它。

实现可迭代接口的数据结构,一般都自身实现或继承了以Symbol.iterator属性的,就属于可迭代对象。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。

一个包含next()方法的对象,才可以称为一个迭代对象。next()对象的会有返回一个对象,对象中包含两个值,如下所示:

  • value:迭代器返回的任何JavaScript值。donetrue时可省略。
  • done:一个布尔值,为false时表示迭代未停止,为true时立即停止迭代器,且可以省略value的值。

JavaScript原生提供的迭代器接口如下图所示:

Generator

Generator是ES2015中提供的一种异步编程解决方案,定义Generator函数在function关键字和函数名中间使用*星号,函数内部使用yield关键字定义不同的状态。

示例代码如下:

js 复制代码
function* testGenerator() {
  // yield定义一个状态
  yield '一碗周'
  yield 'es新特性'
  return 'generator' // 终结Generator,后面即使有yield关键字也无效
}
const g = testGenerator() // 返回 Generator 对象,通过next()方法移动状态

g.next()
/* { value: '一碗周', done: false } */

g.next()
/* { value: 'es新特性', done: false } */

g.next()
/* { value: 'generator', done: true } */

ES7(2016)

1. 幂指数操作符

增加了幂指数操作符来进行幂指数运算:

js 复制代码
console.log(2 ** 3)  // 8

x ** y 产生的结果等同于 Math.pow(x, y)

2. Array.prototype.includes

这个数组方法主要用来判断数组中是否包含某个元素:

js 复制代码
let num = [1, 2, 3, 4, 5]

console.log(num.includes(1))  // true

ES8(2017)

1. 异步函数 async/await

async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

js 复制代码
function timeout(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms)
    })
}

async function asyncPrint(value, ms) {
    await timeout(ms)
    console.log(value)
}

asyncPrint('Hello', 300)

上诉代码指定 300ms 后输出 Hello。

async 函数返回一个 Promise 对象,async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数:

js 复制代码
async function f() {
    return 'Hello'
}

f().then(v => console.log(v))  // 'Hello'

async 函数内部抛出错误会导致返回的 Promise 对象变成 rejected 状态,抛出的错误对象会被 catch 方法回调函数接受到:

js 复制代码
async function f() {
    throw new Error('出错了')
}

f().then(
    v => console.log(v),
    e => console.log(e)
)   // 'Error: 出错了'

正常情况下,await 命令后面是一个 Promise 对象,如果不是,会被转为一个立即 resolve 的 Promise 对象。

await 命令后面的 Promise 对象如果变成 rejected 状态,则 reject 的参数会被 catch 方法的回调函数接收到。

只要一个 await 语句后面的 Promise 变成 rejected,那么整个 async 函数都会被中断执行。

注意:

  • await 命令后面的 Promise 对象的运行结果可能是 rejected,最好把 await 命令放在 try...catch 中。
  • 多个 await 命令后面的异步操作如果不存在继发关系,最好让它们同时触发。
  • await 命令只能用在 async 函数中,如果用在普通函数中就会报错。

2. Object.values()

js 复制代码
Object.values({a: 1, b: 2, c: 3}); // [1, 2, 3]

3. Object.entries()

js 复制代码
Object.entries({a: 1, b: 2, c: 3}); // [["a", 1], ["b", 2], ["c", 3]]

4. Object.getOwnPropertyDescriptors()

5. String padding

js 复制代码
// padStart
'hello'.padStart(10); // "     hello"
// padEnd
'hello'.padEnd(10) "hello     "

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

7. Object.getOwnPropertyDescriptors()

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

8. SharedArrayBuffer对象

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

js 复制代码
/**
 * 
 * @param {*} length 所创建的数组缓冲区的大小,以字节(byte)为单位。  
 * @returns {SharedArrayBuffer} 一个大小指定的新 SharedArrayBuffer 对象。其内容被初始化为 0。
 */
new SharedArrayBuffer(10)

9. Atomics对象

Atomics 对象提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作。

ES9(2018)

1. 异步迭代

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

js 复制代码
async function process(array) {
  for await (let i of array) {
    // doSomething(i);
  }
}

2. Promise.finally()

js 复制代码
Promise.resolve().then().catch(e => e).finally();

3. Rest/Spread 属性

js 复制代码
const values = [1, 2, 3, 5, 6];
console.log( Math.max(...values) ); // 6

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

js 复制代码
const reg = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;
const match = reg.exec('2021-02-23');

5. 正则表达式反向断言

复制代码
(?=p)、(?<=p)  p 前面(位置)、p 后面(位置)
(?!p)、(?<!p>) 除了 p 前面(位置)、除了 p 后面(位置)

(?<=w)


(?<!w)

6. 正则表达式dotAll模式

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

js 复制代码
/hello.world/.test('hello\nworld');  // false

ES10(2019)

1. Array.flat()和Array.flatMap()

flat()

js 复制代码
[1, 2, [3, 4]].flat(Infinity); // [1, 2, 3, 4]

flatMap()

js 复制代码
[1, 2, 3, 4].flatMap(a => [a**2]); // [1, 4, 9, 16]

2. String.trimStart()和String.trimEnd()

去除字符串首尾空白字符

3. String.prototype.matchAll

matchAll()为所有匹配的匹配对象返回一个迭代器

js 复制代码
const raw_arr = 'test1  test2  test3'.matchAll((/t(e)(st(\d?))/g));
const arr = [...raw_arr];

4. Symbol.prototype.description

只读属性,回 Symbol 对象的可选描述的字符串。

js 复制代码
Symbol('description').description; // 'description'

5. Object.fromEntries()

返回一个给定对象自身可枚举属性的键值对数组

js 复制代码
// 通过 Object.fromEntries, 可以将 Map 转化为 Object:
const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
console.log(Object.fromEntries(map)); // { foo: "bar", baz: 42 }

6. 可选 Catch

ES11(2020)

1. Nullish coalescing Operator(空值处理)

表达式在 ?? 的左侧 运算符求值为undefined或null,返回其右侧。

js 复制代码
let user = {
    u1: 0,
    u2: false,
    u3: null,
    u4: undefined
    u5: '',
}
let u2 = user.u2 ?? '用户2'  // false
let u3 = user.u3 ?? '用户3'  // 用户3
let u4 = user.u4 ?? '用户4'  // 用户4
let u5 = user.u5 ?? '用户5'  // ''

2. Optional chaining(可选链)

?.用户检测不确定的中间节点

js 复制代码
let user = {}
let u1 = user.childer.name // TypeError: Cannot read property 'name' of undefined
let u1 = user.childer?.name // undefined

3. Promise.allSettled

返回一个在所有给定的promise已被决议或被拒绝后决议的promise,并带有一个对象数组,每个对象表示对应的promise结果

js 复制代码
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => reject('我是失败的Promise_1'));
const promise4 = new Promise((resolve, reject) => reject('我是失败的Promise_2'));
const promiseList = [promise1,promise2,promise3, promise4]
Promise.allSettled(promiseList)
.then(values=>{
  console.log(values)
});

4. import()

按需导入

5. 新基本数据类型BigInt

任意精度的整数

6. globalThis

  • 浏览器:window
  • worker:self
  • node:global

ES12(2021)

1. replaceAll

返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉

js 复制代码
const str = 'hello world';
str.replaceAll('l', ''); // "heo word"

2. Promise.any

Promise.any() 接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise 。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise

js 复制代码
const promise1 = new Promise((resolve, reject) => reject('我是失败的Promise_1'));
const promise2 = new Promise((resolve, reject) => reject('我是失败的Promise_2'));
const promiseList = [promise1, promise2];
Promise.any(promiseList)
.then(values=>{
  console.log(values);
})
.catch(e=>{
  console.log(e);
});

3. WeakRefs

使用WeakRefs的Class类创建对对象的弱引用(对对象的弱引用是指当该对象应该被GC回收时不会阻止GC的回收行为)

4. 逻辑运算符和赋值表达式

逻辑运算符和赋值表达式,新特性结合了逻辑运算符(&&,||,??)和赋值表达式而JavaScript已存在的 复合赋值运算符有:

js 复制代码
a ||= b
//等价于
a = a || (a = b)

a &&= b
//等价于
a = a && (a = b)

a ??= b
//等价于
a = a ?? (a = b)

5. 数字分隔符

数字分隔符,可以在数字之间创建可视化分隔符,通过_下划线来分割数字,使数字更具可读性

js 复制代码
const money = 1_000_000_000;
//等价于
const money = 1000000000;

1_000_000_000 === 1000000000; // true

ES13(2022)

1.class的扩展

在ES2022中允许我们并不在constructor中定义类的成员,示例代码如下:

js 复制代码
class C {
  myName = '一碗周'
}
/* 两者是一致的 */
class C {
  constructor() {
    myName = '一碗周'
  }
}

如果成员只声明不初始化它的默认值是undefined。

在ES2022中允许我们使用#开头命名的变量作为类的私有成员,示例代码如下:

js 复制代码
class C {
  #myName = '一碗周'
}
const c = new C()
console.log(#myName) //  Private field '#myName' must be declared in an enclosing class

2. await在顶层使用

在ES2022中新增了允许在顶层使用await,在顶层可以不适用async函数进行包裹,示例代码如下:

js 复制代码
import { AsyncFun } from 'module'
await AsyncFun()
console.log(123)

3. Object.hasOwn()

Object.hasOwn()方法用于判断某个对象上是否具有某个属性,示例代码如下:

js 复制代码
const person = {
  name: '一碗周',
  age: 18,
}
console.log(Object.hasOwn(person, 'name')) // true
console.log(Object.hasOwn(person, 'sex')) // false

4. Array.prototype.at()

ES2022中新增的at()方法,它的作用是获取数组中的某个成员,它的参数是数组的索引,与直接使用索引的方式不同,它允许我们传递负值,等同于从后面倒数,示例代码如下:

js 复制代码
const arr = [1, 2, 3, 4, 5, 6]
console.log(arr.at(-1)) // 6
// 等同于 arr[arr.length - 1]

5. d修饰符

正则表达式增加了一个/d修饰符,当使用正则表达式的exec()方法时,如果有/d修饰符,那么结果会多返回一个indices属性,用来表示匹配的结果的在原字符串中的起始index值。示例代码如下:

js 复制代码
const str = 'JavaScript'
const r = /a/d

const m = r.exec(str)
console.log(m.indices[0]) //[ 1, 2 ]

6. 顶层 await

之前await关键字只能在async函数内部使用,在外部使用就会报错: SyntaxError - SyntaxError: await is only valid in async function;

在ES13 允许在模块的顶层使用 await, 并且不再需要配合 async函数使用,它允许模块充当大型异步函数,使得我们在 ESM 模块中可以等待资源的加载,只有资源加载完毕之后才会去执行当前模块的代码。

实际运用:

a、动态加载模块

js 复制代码
const strings = await import(`/i18n/${navigator.language}`);

这对于开发/生产拆分、国际化、环境拆分等非常有用。

b、资源初始化

js 复制代码
const connection = await dbConnector();

配合try catch实现依赖回退

复制代码
let jQuery;
try {
  jQuery = await import('https://cdn-a.com/jQuery');
} catch {
  jQuery = await import('https://cdn-b.com/jQuery');
}

依赖发生故障时,可以回退到旧版本,防止程序崩溃。

7. at()

.at()是数组新增的一个方法,通过传递给定索引来获取数组元素,这个索引可以是正数也可以是负数,当索引是正数时效果和 [] 是一样的,当索引是负数时就会从数组的最后一项开始。

JS 复制代码
const arr = [1,2,3,4,5]; 
arr[arr.length-1] // 5
const arr = [1,2,3,4,5]; 
arr.at(-1) // 5

支持类型

  • String
  • Array
  • Typed Array

ES14(2023)

1. Array.prototype.toSorted

toSorted() 与 Array.prototype.sort() 具有相同的签名,但它创建一个新的数组,而不是对原数组进行操作。

js 复制代码
let arr = [5, 4, 2, 3, 1];
arr === arr.sort(); // true - [1, 2, 3, 4, 5]

arr === arr.toSorted(); // false - [1, 2, 3, 4, 5]

toSorted() 和 sort() 一样,接受一个可选参数作为比较函数。例如,我们可以使用 toSorted() 创建一个按降序排列的新数组

js 复制代码
const numbers = [10, 5, 2, 7, 3, 9, 1, 6, 4];
const sortedNumbers = numbers.toSorted((a, b) => {
  return b - a;
});
console.log(sortedNumbers); // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

需要注意的是,toSorted() 也可以应用于对象数组。这种情况下,需要提供一个使用对象上的数据的比较函数,因为对象没有自然的排序方式。

js 复制代码
// 比较对象
const objects = [
  { name: "John", age: 30 },
  { name: "Jane", age: 25 },
  { name: "Bill", age: 40 },
  { name: "Mary", age: 20 }
];
const sortedObjects = objects.toSorted((a, b) => {
  return a.name.localeCompare(b.name);
});
console.log(sortedObjects);
// [{"name":"Bill","age":40},{"name":"Jane","age":25},{"name":"John","age":30},{"name":"Mary","age":20}]

2. Array.prototype.toReversed

与 toSorted() 和 sort() 一样,toReversed() 是 reverse() 的复制版本。

js 复制代码
["a", "b", "c", "d", "e"].toReversed(); // ['e', 'd', 'c', 'b', 'a']

3. Array.prototype.with

新的 with() 方法允许您基于索引修改单个元素,并返回一个新的数组。因此,如果您知道索引和新值,这个方法非常方便。需要注意的是with() 是 set() 的复制伴侣。

js 复制代码
const arr4 = ["I", "am", "the", "Walrus"];

// 用 "Ape Man" 替换字符串 "Walrus"。
const newArr4 = arr4.with(3, "Ape Man");

console.log(newArr4);

4. Array.prototype.findLast

findLast() 方法允许您从数组中获取匹配元素的最后一个实例。如果找不到匹配元素,则返回 undefined。下面代码中提供了一个简单的示例,我们从数组中获取最后一个偶数。

js 复制代码
const arr = [54, 34, 55, 75, 98, 77];

const lastEvenIndex = arr.findLast((element) => {
  return element % 2 === 0;
});

console.log(lastEvenIndex); // 98

findLast() 还支持传递一个 "thisArg" 来设置上下文。也就是说,第二个参数将告诉第一个参数函数 this 关键字将引用什么。从下面代码中看到这一点,我们使用一个自定义对象来找到可以被 5 整除的第一个元素。

js 复制代码
const arr6 = [54, 34, 55, 75, 98, 77];
const myObject = { testCase: 5 };
const lastEvenIndex = arr5.findLast((element) => {
  return element % myObject.testCase === 0;
}, myObject);

console.log(lastEvenIndex); // 75

5.Array.prototype.findLastIndex

findLastIndex() 的工作方式与 findLast() 相同,只是它返回匹配元素的索引而不是元素本身。例如,下面例子展示如何找到最后一个可以被 6 整除的元素的索引。

复制代码
代码清单 8. 使用 findLastIndex() 找到元素的索引

const arr = [54, 34, 55, 75, 98, 77];
arr.findLastIndex(x => x % 6 === 0); // 0

6.Array.prototype.toSpliced

上面新增的所有方法也适用于 TypedArray,但 toSpliced() 这个新的数组方法只存在于 Array 中。toSpliced() 方法是 splice() 的复制版本,splice()是js中数组操作常用的方法;

假设我们有一个颜色数组,需要在中间插入一些新的颜色(粉红色和青色)。这会创建一个新数组,而不是修改原始数组。

js 复制代码
const arr = ["red", "orange", "yellow", "green", "blue", "purple"];
const newArr = arr.toSpliced(2, 1, "pink", "cyan");
console.log(newArr);
// ["red", "orange", "pink", "cyan", "green", "blue", "purple"]
console.log(newArr[3]);
// 'cyan'
console.log(arr[3]);
// 'green'

7.正式的 shebang 支持

Shebang 是旧式 Unix 术语,表示井号后跟一个感叹号:#!(其中 "bang" 是感叹号的俚语)。

js 复制代码
#!/bin/bash

echo "Hello, world!"

您可以使用 ./hello.sh 直接运行上面代码。

您也可以在 JavaScript 中执行类似操作,如代码清单 11 所示。

js 复制代码
#!/usr/bin/env node

console.log("Hello, world!");

上面例子告诉操作系统使用 node 程序来运行此脚本。只需键入 ./hello.js 即可运行它。如果没有 #!是无法运行的。

Shebang 支持是规范中已经实现并在多种情境下非正式采用的特性之一。

8.Symbol 作为 WeakMap 的键

ES14中的最后一个新功能是Symbol 可以作为 WeakMap 的键使用。在这之前,WeakMap仅允许对象作为键值,新特性更容易创建和共享key。

复制代码
var map = new WeakMap(); // 创建一个弱映射
function useSymbol(symbol){
    doSomethingWith(symbol);
    var called = map.get(symbol) || 0

上面的例子允许从外部调用者调用计数器,并在不再有引用时释放映射条目。代码本身无法知道何时不再需要引用,如果使用普通的 Map,将会导致内存泄漏。这是因为即使在调用它的客户端不再需要引用时,代码仍然会保留对该引用的持有。在这种情况下使用 WeakMap,可以确保垃圾回收在不再存在对键符号的引用时删除映射条目。

9. Record 和 Tuple 类型:

js 复制代码
const person = {
  name: "John Doe",
  age: 30,
  hobbies: ["coding", "reading", "playing guitar"]
};

这段代码定义了一个名为 person 的 Record,其中包含三个元素:姓名、年龄和爱好。name 元素是一个字符串,age 元素是一个整数,hobbies 元素是一个字符串数组。

以下是 ES14 之后的 ECMAScript 2023 中的元组示例:

js 复制代码
const colors = ["red", "green", "blue", "purple"];

此代码定义了一个名为颜色的元组,其中包含四个元素:红色、绿色、蓝色和紫色。元组的元素现在可以是任何类型的数据。

参考:

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

ES14新特性揭秘,对前端开发有哪些影响?

相关推荐
it_remember1 小时前
新建一个reactnative 0.72.0的项目
javascript·react native·react.js
敲代码的小吉米2 小时前
前端上传el-upload、原生input本地文件pdf格式(纯前端预览本地文件不走后端接口)
前端·javascript·pdf·状态模式
da-peng-song2 小时前
ArcGIS Desktop使用入门(二)常用工具条——数据框工具(旋转视图)
开发语言·javascript·arcgis
低代码布道师4 小时前
第五部分:第一节 - Node.js 简介与环境:让 JavaScript 走进厨房
开发语言·javascript·node.js
满怀10154 小时前
【Vue 3全栈实战】从响应式原理到企业级架构设计
前端·javascript·vue.js·vue
伟笑5 小时前
elementUI 循环出来的表单,怎么做表单校验?
前端·javascript·elementui
确实菜,真的爱5 小时前
electron进程通信
前端·javascript·electron
魔术师ID7 小时前
vue 指令
前端·javascript·vue.js
Clown957 小时前
Go语言爬虫系列教程 实战项目JS逆向实现CSDN文章导出教程
javascript·爬虫·golang
星空寻流年8 小时前
css3基于伸缩盒模型生成一个小案例
javascript·css·css3