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

相關文章
相關標籤/搜索