ES2015(ES6)以后特性的合集(很详细)

1、ES 2016

一、Array.prototype.includes 和 String.prototype.includes

在以前咱们比较一个数组中是否包含某个元素的经常使用作法是:javascript

if (arr.indexOf(el) !== -1) {
    ...
}

或者java

if (~arr.indexOf(el)) {
    ...
}
//此处~ 为取反,即-1取反结果为0,0取反为-1,1取反会-2,2取反为-3

indexOf 结果返回是个数字,咱们必需要和其余数字进行比较,才能见到获得是否"存在",即-1 表示不存在,大于-1 表示存在。node

因为 indexOf 在查找元素的比对过程当中,使用的是严格相等"===",好比webpack

[1,2,3].indexOf('1')   //就会找不到。由于1和'1'并不相等。

一样在当数组中含有 NaN 是,也找不到,即git

[NaN].indexOf(NaN)   //找不到,由于NaN===NaN为false。

因而可知 indexOf 并不能完美的找出是否包含某个元素值。github

因此咱们便有了下面的方式web

arr.includes(el);

includes 结果直接返回 boolean,true 表示存在,false 表示不存在,很是直观。includes 是经过,类型和值是否“一致”,而不是“严格相等”正则表达式

[1,2,3].includes('1')   //false
[1,2,3].includes(1)   //true
[1,2,NaN].includes(NaN)   //true

因此再之后的使用中,
indexOf 应倾向于寻找 元素的位置,可能后续还要经过这个位置进行其余计算的场景,而 includes 应倾向于寻找 元素是否存在,只须要知道存在或者不存在,其余没必要知道chrome

既然数组有 indexOf,字符串也有 indexOf,那么一样
数组有 includes,字符串也有 includesjson

"1234567".includes("12")   //true
二、指数运算符

在之前,js 要求取 m 的 n 次方,得这么写

Math.pow(m,n)

好比 2 的 3 次方

Math.pow(2,3)  //8

而如今只须要 指数操做符"**",即两个星号便可

2 ** 3  //表示2的3次方
3 ** 2  //表示3的2次方

因为- + _ /,都有 -=,+=, _=, /=

一样 也有 "="

let a = 2;
a **= 2; //  a = a ** 2

2、ES 2017

一、Object.values / Object.keys() / Object.entries

将 object 的 key 或者 value 转成一个能够遍历的数组。

Object.values({id:1,name:2});    // [1,2]
Object.keys({id:1,name:2});    // ["id", "name"]
Object.entries({id:1,name:2});    // [["id", 1], ["name", 2]]
二、字符串“填充”
  • String.prototype.padStart(); 使用给定的字符串,填充原字符串头部,总体达到必定的长度
  • String.prototype.padEnd();使用给定的字符串,填充原字符串尾部,总体达到必定的长度
  • String.prototype.trimRight();移除尾部的空白(和 trimEnd 同样,为了兼容其余浏览器)
  • String.prototype.trimLeft();移除首部的空白(和 trimStart 同样,为了兼容其余浏览器)
  • String.prototype.trimEnd(); 移除尾部的空白
  • String.prototype.trimStart();移除首部的空白
  • String.prototype.trim(); 移除两端的空白
//padStart
"abc".padStart(10); // "       abc"  在原字符串"abc"的头部填充空白,以使整个字符串的长度变为10
"abc".padStart(10, "foo"); // "foofoofabc"  在原字符串"abc"的头部填充"foo",以使整个字符串的长度变为10
"abc".padStart(6, "123465"); // "123abc"   在原字符串"abc"的头部填充"123465",以使整个字符串的长度变为10,若是中途够了,就不进行填充了
"abc".padStart(8, "0"); // "00000abc"
"abc".padStart(1); // "abc"
//padEnd
"abc".padEnd(10); // "abc       "   同padStart,只不过填充到末尾
"abc".padEnd(10, "foo"); // "abcfoofoof"   同padStart,只不过填充到末尾
"abc".padEnd(6, "123456"); // "abc123"       同padStart,只不过填充到末尾
"abc".padEnd(1); // "abc"          同padStart,只不过填充到末尾
//trimStart
"   abc   ".trimStart(); // "abc   "
"   abc   ".trimEnd(); //  "   abc"
"   abc   ".trim(); // "abc"
三、Object.getOwnPropertyDescriptors

获取对象的全部自身属性的描述符。包括 configurable(可配置),enumerable(可枚举),writable(可写)

Object.getOwnPropertyDescriptors({ id: 1, name: 2 });

/*
{
  "id": {
    "value": 1,
    "writable": true,
    "enumerable": true,
    "configurable": true
  },
  "name": {
    "value": 2,
    "writable": true,
    "enumerable": true,
    "configurable": true
  }
}
*/
四、在函数末尾参数后增长逗号

在以前的函数是这样的

function clownPuppiesEverywhere(param1, param2) {}

//或者这样
function clownPuppiesEverywhere(param1, param2) {}

param2 后面若是加逗号会报错,而如今不会报错,它是被容许的

function clownPuppiesEverywhere(param1, param2) {}

//或者这样
function clownPuppiesEverywhere(param1, param2) {}

这样若是咱们有新参数,只须要日后加就好了,没必要往上一行末尾加逗号(真懒,加个逗号又不费事,鸡肋特性)

五、async / await

将异步变为同步神器。不再须要像 promise 那样回调了

async function getList(){ //用async指明当前方法中有异步,可是当作同步执行

    await getId();  //若是getId是个promise,则只有状态为resolve时,才进入下一行。若是是普通函数,则默认是resolved


    let result = await getName(); //还能够有返回值。其值为resolved(xx)中的参数

}

await 的并行处理

//由于多个await都是依次日后台发送。若是这几个请求以前没有前后关联顺序,则彻底不必。能够作成并行发送
async getName() {
  let str = "";
  await Promise.all([this.getPromise(),this.getPromise(),this.getPromise()]);
  return str;
}
六、共享内存和原子

太复杂,我也没看懂

若是你想查看,这是详细
文档

3、ES 2018

一、正则表达式 点匹配符 s

在正则表达式中,“.” 点,表明 匹配除换行符以外的任何单个字符。
以下:

/.n/.test('an');   //. 匹配到了a
/.n/.test('1n');   //. 匹配到了1
/.n/.test('nn');   //. 匹配到了n
/.n/.test('\nn');   //.不会匹配 \n 返回false

假若有个需求,须要匹配任何字符。那怎么办.....

有个骚操做。

/foo[^]bar/.test('foo\nbar');  //返回true

用 [^] 替换掉. 它表示匹配包含n 在内的任意字符。

可这也太秀了,正常人都想不到这么写。这么写出来,别人也看不懂。

因此便有了下面的写法:

/foo.bar/s.test('foo\nbar');    //返回true

正则的后面加个 s 标志,你能够将 s 理解为 stop,终止的意思,即表示除了其余的,还能匹配终止符 n,换句话说如今这个. 啥都能匹配到

咱们能够总结下,到此为止,正则表达式标志符有:

g      全局搜索。
i      不区分大小写搜索。
m      多行搜索。
s      容许 . 匹配换行符。
u      使用unicode码的模式进行匹配。
y      执行“粘性”搜索,匹配从目标字符串的当前位置开始,可使用y标志。
二、扩展运算符
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x; // 1
y; // 2
z; // { a: 3, b: 4 }



let n = { x, y, ...z };
n; // { x: 1, y: 2, a: 3, b: 4 }

或者

let [x,y,...z] = [ 1,2,3,4 ];
x; // 1
y; // 2
z; // [3, 4]



let n = [ x, y, ...z ];
n; // [1, 2, 3, 4]

...x 始终表明这是个展开式,而 x 表明原式(对象,或者数组),

相似于高数中的泰勒展开式, ...x 就是那个展开式,x 就是那个原式。

好比:

function getName({a,b,...others}){
    console.log(others)
}

getName({a:1,b:2,c:3,d:4})

因为...others 是展开式 c:3,d:4,那么 others 天然就是原式{c: 3, d: 4}

三、Promise.prototype.finally

在 promise 结束时,不管结果是 resovled 或者是 rejected,finally 都会执行指定的回调函数。

这避免了一样的语句须要在 then()和 catch()中各写一次的状况。

因此 finally 中最适合须要进行"清空"的操做。好比 request 请求完成或失败后,都要将 loading 设置为 false。或者关闭打开的文件流对象,或者进行日志记录等。

finally()方法和 then()同样,执行后,也返回一个 Promise。

因此通常流程是

new Promise((resolved, rejected)=>{
    //...
    //return  resolved(res); 若是执行成功
    //return  rejected(); 若是执行失败
    //...
}).then(res=>{
    //resolved的回调, res的值为resolved传入的参数
}).catch(err=>{
    //rejected的回调。err为rejected传入的失败的缘由
}).finally(()=>{
    //因为不关注resolved仍是rejected。因此没有参数。

})
四、for-await-of

咱们常常会经过 for....of 进行迭代,以下:

let arr = [1,2,3];
for(let num of arr) {
   console.log(num);
}

输出 1,2,3

咱们能够想象下,加入 arr 中的每一项不是普通类型的对象,而是 promise 呢?

//分别建立三个promise对象
let p1 = new Promise((resolved,rejected)=>{
    setTimeout(()=>{
       resolved(1)
    },1000)
});
let p2 = new Promise((resolved,rejected)=>{
    setTimeout(()=>{
       resolved(2)
    },1000)
});
let p3 = new Promise((resolved,rejected)=>{
    setTimeout(()=>{
       resolved(3)
    },1000)
});

let arr = [p1,p2,p3];
for(let num of arr) {
   console.log(num);
}

那执行结果会怎么样呢?

你将会在控制台直接看到下面三个结果。

> Promise {<pending>}
> Promise {<pending>}
> Promise {<pending>}

也就是说 arr 在迭代时,分别将 p1,p2,p3 输出控制台了,而 p1,p2,p3 是谁,固然是 promise 啊,只不过还没执行 resolved 而已,处于 pending 状态。

若是咱们想在 arr 迭代时,依次输出 p1,p2,p3 的 resolved 或者 rejected 结果呢?

咱们可使用下面的方式

//分别建立三个promise对象
let p1 = new Promise((resolved,rejected)=>{
    setTimeout(()=>{
       resolved(1)
    },1000)
});
let p2 = new Promise((resolved,rejected)=>{
    setTimeout(()=>{
       resolved(2)
    },1000)
});
let p3 = new Promise((resolved,rejected)=>{
    setTimeout(()=>{
       resolved(3)
    },1000)
});

let arr = [p1,p2,p3];
for await(let num of arr) {
   console.log(num);
}

在 for 后面,左圆括号前面,加个 await。

这样在每次 arr 迭代时,等待 num 的结果为 resolved 或者 rejected 时,才进入 for 的语句块。

结果输出为: 1 2 3

咱们能够想一想在没有 for-await-of 以前,是怎么用的?

let num = null;
let arr = [p1,p2,p3];

num = await arr[0];
console.log(num);

let num = await arr[1];
console.log(num);

let num = await arr[2];
console.log(num);

是否是?可是若是 arr 中几个这么写还行,可若是多了呢,显然不如 for await of 方便。

4、ES 2019

一、可选的 catch 参数
try {
  throw new Error("出错了")
} catch (err) {
  console.log(err.message);
}

咱们一般会在 catch 中经过 err 来获取或者打印错误信息。

可是有些场景下咱们不须要 catch 中的 err,信息,只是捕获错误,而后作些其余的事。此时 err 参数被闲置,unused,未被使用。

针对这种状况,es 规范容许你省略 err 参数。

try {
  throw new Error("出错了")
} catch () {
  console.log('321');
}

==Uncaught SyntaxError: Unexpected token ')'==

报错了,纳尼?

注意不是上面这种省略,而应该是下面的省略。

try {
  throw new Error("出错了")
} catch{
  console.log('321');
}

catch 后面直接跟{便可。

坑我给大家填上了,别在掉进去了。

二、json 超集

因为 json 能够支持 "u2028"(行分隔符)和 "u2029"(段落分隔符)两个特殊字符。

咱们能够看看这两个字符长什么样
image
方方正正的,一个写着 LSEP,一个写着 PSEP.

建立个 json,包含 u2028.

image

没问题,能够解析。

可是这两个字符以前在 js 中是不能写的。因此 ECMA 在标准中实现了他们,以便在 js 中也能正常使用。

image

三、Symbol.prototype.description

symbol 是一种基本数据类型。 每一个从 Symbol()返回的 symbol 值都是惟一的。一个 symbol 值能做为对象属性的标识符;这是该数据类型仅有的目的。

咱们能够经过下面方式获取 symbol 的描述。

Symbol('desc').description    //desc
四、Function.prototype.toString 的修订

之前因为 function 继承了从 object 来的 toString 方法;因此 toString 为"[object Object]"

function getName(){
    return "hello";
};
getName.toString()    // "[object Object]"

而修订后的 function toString 覆盖了从 object 继承来的 toString 方法。返回一个包含定义函数的源文本段。

image

五、Object.fromEntries
let obj = {
  id: 1,
  name: "hello",
};
let entries = Object.entries(obj); //   [["id",1],["name","hello"]]

一样咱们可使用 fromEntries 还原 obj

let obj = Object.fromEntries([
  ["id", 1],
  ["name", "hello"],
]); // {id: 1, name: "hello"}
六、Array.prototype.flat / Array.prototype.flatMap

具体可参考 mdn 连接

flat 文档

flatMap 文档

5、ES 2020

一、String.prototype.matchAll
let regexp = /te/;
let str = 'test1test2';

str.match(regexp)   // ['te']  匹配到第一个位置就中止,并返回匹配到的字符串

加个 g 标志

let regexp = /te/g;
let str = 'test1test2';

str.match(regexp)   //["te", "te"]   返回全部匹配到的字符串

matchAll 会返回一个包含全部匹配正则表达式的结果及分组捕获组的迭代器。且正则表达式必须含有标志 g,不然报错

let regexp = /te/g;
let str = 'test1test2';

str.matchAll(regexp)   //  RegExpStringIterator{}  但会迭代器。

能够经过解构方式取出迭代器。

let regexp = /te/g;
let str = 'test1test2';

let arr = [...str.matchAll(regexp) ]   //
arr[0]  //  {0: "te", groups: undefined, index: 0, input: "test1test2", length: 1}

arr[1]  //  {0: "te", groups: undefined, index: 5, input: "test1test2", length: 1}

这样咱们不只能获取到 匹配的字符串,仍是经过 index 知道匹配的位置。

二、import()

在 js 中,引入模块的方式一般是:

import _ from "lodash"

在代码运行以前,在编译阶段,就已经绑定引入本地做用域。

可是若是咱们想在运行阶段动态的引入呢?

答案是没有办法。

若是你使用的是 webpack,则可使用 webpack 的 require("xxx") 动态的将模块引入。

因此 ECMA 直接在 js 核心库中,实现了这个 api。即便不适用 webpack,也能直接在浏览器上运行 import().

在 chrome 控制台咱们能够直接使用。

image

同时发现,import()返回的是个 promise 对象,

因此咱们能够这么用

import("/static/js/app.js").then(res=>{
    //xxx
})

或者
await import("/static/js/app.js");
//后续代码
三、BigInt

在 javascript 中,interget 的最大数为 2^53

Math.pow(2,53)  // 9007199254740992

若是加 1,发现没变。

Math.pow(2,53)+1  // 9007199254740992

假如要表示 比 Math.pow(2,53) 还要大的数值怎么办?

可使用 BigInt 类型来表示

const theBiggestInt = 9007199254740991n;   //后面追加 n,表示这是个bigInt类型

const alsoHuge = BigInt(9007199254740991);  //直接经过构造器建立。

在 bigInt 中,最小的 1,用 1n 来表示,以下面的运算

1n+1n==2n
2n-1n == 1n
2n * 2n == 4n
4n / 2n == 2n

9007199254740992n +1n==9007199254740993n   //  //2^53加1  结果为

特殊状况

1n+1n==2  //true
0n == 0   //true

//可是下面为false
1n+1n==2    //false
0n === 0    //false

特别的

1n+1    会直接报错

Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
三、Promise.allSettled

在 promise 中的组合器有下面几种:

  • Promise.all 参数中的全部 promise 完成,才执行回调。
  • Promise.race 只要有一个完成,或者一个失败,就立刻执行回调。
  • Promise.all 只要有个完成,或者全部的失败,才执行回调。
  • Promise.allSettled 永远不会中途结束,会所有完成,返回全部给定的 promise 已被完成或失败后的数组。
const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, "1oo");
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(reject, 1000, "foo");
});
const promises = [promise1, promise2];

Promise.allSettled(promises).then((results) => {
  //results  [{"status":"fulfilled","value":"1oo"},{"status":"rejected","reason":"foo"}]
});
三、globalThis

在之前,从不一样的 JavaScript 环境中获取全局对象须要不一样的语句。在 Web 中,能够经过 window、self 或者 frames 取到全局对象,可是在 Web Workers 中,只有 self 能够。在 Node.js 中,它们都没法获取,必须使用 global。

globalThis 提供了一个标准的方式来获取不一样环境下的全局 this 对象(也就是全局对象自身)

这样在浏览器中, globalThis 就是 window 或者 self,
在 Web Workers 中, globalThis 就是 self,
在 nodejs 中,globalThis 就是 global。

快去把你项目中的 window,self,global 等替换成 globalThis 吧

四、for-in

for...in 语句以任意顺序遍历一个对象的除 Symbol 之外的可枚举属性。

在对象中使用 for....in 并无什么问题

for(let item in {id:1,name:'123'}){
   console.log(item)  //依次输出 id, name
}

不建议 for....in 和数组搭配一块儿使用。为何呢,请看下面的例子:

for(let item in ['a','b','c']){
   console.log("-===",item) //依次输出 '0' '1' '2',

   //注意  '0' '1' '2'并不是下标,而是枚举位置
}

咱们再给数组添对象添加个可枚举属性

let arr = [1,2,3];
Object.defineProperty(arr, "getName", {
    value: "hello",
    enumerable: true
});

//等同于

let arr = [1,2,3];
Array.prototype.getName = "hello";


//紧接着咱们执行下面
for(let item in arr){
   console.log(item)  //依次输出 '0' '1'  '2'  'getName'
}

发现没?? 是不出错了,若是咱们项目中,在 util 的模块中,给 Array.prototype 上增长了一些 polyfill,那么咱们使用 for....in 遍历数组,就必然出错。

固然咱们能够经过其余方式避免。
好比

for(let i=0;i<arr.length;i++){
    //
}

//或者
arr.forEach((value,index)=>{
  //
})
五、可选链操做符 '?.'

在之前咱们进行条件判断时:

let obj = { "circle": { "x": 0, "y": 0 }, "radius": 50 };

if(obj && obj.circle && obj.circle.x){

    //而后才敢拿着  obj.circle.x 进行操做。不然假如obj为null,obj.circle 就会直接报错。

}

而如今不用这么麻烦

let obj = { "radius": 50 };

if(obj?.circle?.x){
    // obj.circle.x 进行操做
}

?.表示是否有这个属性,若是没有(undifined),或者有,但值为 null。则直接短路不会再继续向下。从而保护了代码不被出错。

let obj = { "circle": { "y": 0 }, "radius": 50 };

if(obj?.circle?.x){

}else{
    //不会报错。在执行到 ?.circle时获得undefiend,直接短路。返回undefiend, 进入 else
}

一样,数组也能够

let arr = ['a','b'];
arr?.[1]  //  'b'

最后注意,可选链不能用于赋值操做

let arr = ['a','b'];
arr?.[1]  = 'c'  //报错   Uncaught SyntaxError: Invalid left-hand side in assignment
六、空值合并操做符 '??'

它是个逻辑操做符,相似于 ||

|| 操做符,只有当左侧为假值(0, '', NaN, false, null,undefined)是才返回右侧的值.

0 || 'A' //A
'' || 'A' //A
NaN || 'A' //A
false || 'A' //A
null || 'A' //A
undefined || 'A' //A

而 ??只有左侧为空值(只有 null 和 undefined)时才

0 ?? 'A';     // 0
'' ?? 'A'      // ''
NaN ?? 'A'     //NaN
false ?? 'A'    //false
null ?? 'A'     //A
undefined ?? 'A' //A
六、import.meta

具体查看
文档

参考 ECMA 规范:
https://github.com/tc39/proposals/blob/master/finished-proposals.md

相关文章
相关标签/搜索