ES6 到 ES10 的新特性總結(jié)

作者:魚(yú)頭的Web海洋 公號(hào) / 陳大魚(yú)頭

介紹

ECMAScript是一種由Ecma國(guó)際(前身為歐洲計(jì)算機(jī)制造商協(xié)會(huì))在標(biāo)準(zhǔn)ECMA-262中定義的腳本語(yǔ)言規(guī)范菇晃。這種語(yǔ)言在萬(wàn)維網(wǎng)上應(yīng)用廣泛捎谨,它往往被稱(chēng)為JavaScript或JScript,但實(shí)際上后兩者是ECMA-262標(biāo)準(zhǔn)的實(shí)現(xiàn)和擴(kuò)展济欢。

歷史版本

除開(kāi)2020年發(fā)布的ES11(由于單獨(dú)寫(xiě)一篇文章概述)脆贵,在此之前共有十個(gè)ECMA-262版本發(fā)表噪矛。其歷史版本如下:

1.1997年6月:第一版
2.1998年6月:修改格式嗜浮,使其與ISO/IEC16262國(guó)際標(biāo)準(zhǔn)一樣
3.1999年12月:強(qiáng)大的正則表達(dá)式,更好的詞法作用域鏈處理摩疑,新的控制指令危融,異常處理,錯(cuò)誤定義更加明確雷袋,數(shù)據(jù)輸出的格式化及其它改變
4.2009年12月:添加嚴(yán)格模式("use strict")吉殃。修改了前面版本模糊不清的概念。增加了getters楷怒,setters蛋勺,JSON以及在對(duì)象屬性上更完整的反射。
5.2011年6月:ECMAScript標(biāo)5.1版形式上完全一致于國(guó)際標(biāo)準(zhǔn)ISO/IEC 16262:2011鸠删。
6.2015年6月:ECMAScript 2015(ES2015)抱完,第 6 版,最早被稱(chēng)作是ECMAScript 6(ES6)刃泡,添加了類(lèi)和模塊的語(yǔ)法巧娱,其他特性包括迭代器碉怔,Python風(fēng)格的生成器和生成器表達(dá)式,箭頭函數(shù)禁添,二進(jìn)制數(shù)據(jù)撮胧,靜態(tài)類(lèi)型數(shù)組,集合(maps老翘,sets 和 weak maps)芹啥,promise,reflection 和 proxies铺峭。作為最早的 ECMAScript Harmony 版本墓怀,也被叫做ES6 Harmony。
7.2016年6月:ECMAScript 2016(ES2016)卫键,第 7 版傀履,多個(gè)新的概念和語(yǔ)言特性。
8.2017年6月:ECMAScript 2017(ES2017)永罚,第 8 版,多個(gè)新的概念和語(yǔ)言特性卧秘。
9.2018年6月:ECMAScript 2018 (ES2018)呢袱,第 9 版,包含了異步循環(huán)翅敌,生成器羞福,新的正則表達(dá)式特性和 rest/spread 語(yǔ)法。
10.2019年6月:ECMAScript 2019 (ES2019)蚯涮,第 10 版治专。

發(fā)展標(biāo)準(zhǔn)

TC39(Technical Committee 39)是一個(gè)推動(dòng)JavaScript發(fā)展的委員會(huì),它的成語(yǔ)來(lái)自各個(gè)主流瀏覽器的代表成語(yǔ)遭顶。會(huì)議實(shí)行多數(shù)決张峰,每一項(xiàng)決策只有大部分人同意且沒(méi)有強(qiáng)烈反對(duì)才能去實(shí)現(xiàn)。

TC39成員制定著ECMAScript的未來(lái)棒旗。

每一項(xiàng)新特性最終要進(jìn)入到ECMAScript規(guī)范里喘批,需要經(jīng)歷5個(gè)階段,這5個(gè)階段如下:

?Stage 0: Strawperson

只要是TC39成員或者貢獻(xiàn)者铣揉,都可以提交想法

?Stage 1: Proposal

這個(gè)階段確定一個(gè)正式的提案

?Stage 2: draft

規(guī)范的第一個(gè)版本饶深,進(jìn)入此階段的提案大概率會(huì)成為標(biāo)準(zhǔn)

?Stage 3: Candidate

進(jìn)一步完善提案細(xì)則

?Stage 4: Finished

表示已準(zhǔn)備好將其添加到正式的ECMAScript標(biāo)準(zhǔn)中

由于ES6以前的屬性誕生年底久遠(yuǎn),我們使用也比較普遍逛拱,遂不進(jìn)行說(shuō)明敌厘,ES6之后的語(yǔ)言風(fēng)格跟ES5以前的差異比較大,所以單獨(dú)拎出來(lái)做個(gè)記錄朽合。

ES6(ES2015)

ES6是一次重大的革新俱两,比起過(guò)去的版本饱狂,改動(dòng)比較大,本文僅對(duì)常用的API以及語(yǔ)法糖進(jìn)行講解锋华。

Let 和 Const

在ES6以前嗡官,JS只有var一種聲明方式,但是在ES6之后毯焕,就多了letconst這兩種方式衍腥。用var定義的變量沒(méi)有塊級(jí)作用域的概念,而letconst則會(huì)有纳猫,因?yàn)檫@三個(gè)關(guān)鍵字創(chuàng)建是不一樣的婆咸。

區(qū)別如下:

{
    var a = 10
    let b = 20
    const c = 30
}
a // 10
b // Uncaught ReferenceError: b is not defined
c // c is not defined
let d = 40
const e = 50
d = 60
d // 60
e = 70 // VM231:1 Uncaught TypeError: Assignment to constant variable.
image.png

類(lèi)(Class)

在ES6之前,如果我們要生成一個(gè)實(shí)例對(duì)象芜辕,傳統(tǒng)的方法就是寫(xiě)一個(gè)構(gòu)造函數(shù)尚骄,例子如下:

function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.information = function () {
    return 'My name is ' + this.name + ', I am ' + this.age
}

但是在ES6之后,我們只需要寫(xiě)成以下形式:


class Person {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    information() {
        return 'My name is ' + this.name + ', I am ' + this.age
    }
}

箭頭函數(shù)(Arrow function)

箭頭函數(shù)表達(dá)式的語(yǔ)法比函數(shù)表達(dá)式更簡(jiǎn)潔侵续,并且沒(méi)有自己的this倔丈,argumentssupernew.target状蜗。這些函數(shù)表達(dá)式更適用于那些本來(lái)需要匿名函數(shù)的地方需五,并且它們不能用作構(gòu)造函數(shù)。

在ES6以前轧坎,我們寫(xiě)函數(shù)一般是:

var list = [1, 2, 3, 4, 5, 6, 7]
var newList = list.map(function (item) {
    return item * item
})

但是在ES6里宏邮,我們可以:

const list = [1, 2, 3, 4, 5, 6, 7]
const newList = list.map(item => item * item)

看,是不是簡(jiǎn)潔了不少,而且箭頭函數(shù)做回調(diào)的時(shí)候沒(méi)有this的指向問(wèn)題缸血。

函數(shù)參數(shù)默認(rèn)值(Function parameter defaults)

在ES6之前蜜氨,如果我們寫(xiě)函數(shù)需要定義初始值的時(shí)候,需要這么寫(xiě):

function config (data) {
    var data = data || 'data is empty'
}

這樣看起來(lái)也沒(méi)有問(wèn)題捎泻,但是如果參數(shù)的布爾值為false時(shí)就會(huì)出問(wèn)題飒炎,例如我們這樣調(diào)用config:

config(0)
config('')

那么結(jié)果就永遠(yuǎn)是后面的值

如果我們用函數(shù)參數(shù)默認(rèn)值就沒(méi)有這個(gè)問(wèn)題,寫(xiě)法如下:

const config = (data = 'data is empty') => {}

模板字符串(Template string)

在ES6之前笆豁,如果我們要拼接字符串厌丑,則需要像這樣:

var name = 'kris'
var age = 24
var info = 'My name is ' + this.name + ', I am ' + this.age

但是在ES6之后,我們只需要寫(xiě)成以下形式:

const name = 'kris'
const age = 24
const info = `My name is ${name}, I am ${age}`

解構(gòu)賦值(Destructuring assignment)

我們通過(guò)解構(gòu)賦值, 可以將屬性/值從對(duì)象/數(shù)組中取出,賦值給其他變量渔呵。

比如我們需要交換兩個(gè)變量的值怒竿,在ES6之前我們可能需要:

var a = 10
var b = 20
var temp = a
a = b
b = temp

但是在ES6里,我們有:

let a = 10
let b = 20
[a, b] = [b, a]

是不是方便很多

模塊化(Module)

在ES6之前扩氢,JS并沒(méi)有模塊化的概念耕驰,有的也只是社區(qū)定制的類(lèi)似CommonJS和AMD之類(lèi)的規(guī)則。例如基于CommonJS的NodeJS:

// circle.js
// 輸出
const { PI } = Math
exports.area = (r) => PI * r ** 2
exports.circumference = (r) => 2 * PI * r

// index.js
// 輸入
const circle = require('./circle.js')
console.log(`半徑為 4 的圓的面積是 ${circle.area(4)}`)

在ES6之后我們則可以寫(xiě)成以下形式:

// circle.js
// 輸出
const { PI } = Math
export const area = (r) => PI * r ** 2
export const circumference = (r) => 2 * PI * r

// index.js
// 輸入
import {
    area
} = './circle.js'
console.log(`半徑為 4 的圓的面積是: ${area(4)}`)

擴(kuò)展操作符(Spread operator)

擴(kuò)展操作符可以在函數(shù)調(diào)用/數(shù)組構(gòu)造時(shí), 將數(shù)組表達(dá)式或者string在語(yǔ)法層面展開(kāi)录豺;還可以在構(gòu)造字面量對(duì)象時(shí), 將對(duì)象表達(dá)式按key-value的方式展開(kāi)朦肘。

比如在ES5的時(shí)候饭弓,我們要對(duì)一個(gè)數(shù)組的元素進(jìn)行相加,在不使用reduce或者reduceRight的場(chǎng)合媒抠,我們需要:

function sum(x, y, z) {
    return x + y + z;
}
var list = [5, 6, 7]
var total = sum.apply(null, list)

但是如果我們使用擴(kuò)展操作符弟断,只需要如下:

const sum = (x, y, z) => x + y + z
const list = [5, 6, 7]
const total = sum(...list)

非常的簡(jiǎn)單,但是要注意的是擴(kuò)展操作符只能用于可迭代對(duì)象

如果是下面的情況趴生,是會(huì)報(bào)錯(cuò)的:

var obj = {'key1': 'value1'}
var array = [...obj] // TypeError: obj is not iterable

對(duì)象屬性簡(jiǎn)寫(xiě)(Object attribute shorthand)

在ES6之前阀趴,如果我們要將某個(gè)變量賦值為同樣名稱(chēng)的對(duì)象元素,則需要:

var cat = 'Miaow'
var dog = 'Woof'
var bird = 'Peet peet'

var someObject = {
  cat: cat,
  dog: dog,
  bird: bird
}

但是在ES6里我們就方便很多:

let cat = 'Miaow'
let dog = 'Woof'
let bird = 'Peet peet'

let someObject = {
  cat,
  dog,
  bird
}

console.log(someObject)

//{
//  cat: "Miaow",
//  dog: "Woof",
//  bird: "Peet peet"
//}

非常方便

Promise

Promise 是ES6提供的一種異步解決方案苍匆,比回調(diào)函數(shù)更加清晰明了刘急。

Promise 翻譯過(guò)來(lái)就是承諾的意思,這個(gè)承諾會(huì)在未來(lái)有一個(gè)確切的答復(fù)浸踩,并且該承諾有三種狀態(tài)叔汁,分別是:

1.等待中(pending)2.完成了 (resolved)3.拒絕了(rejected)

這個(gè)承諾一旦從等待狀態(tài)變成為其他狀態(tài)就永遠(yuǎn)不能更改狀態(tài)了,也就是說(shuō)一旦狀態(tài)變?yōu)?resolved 后检碗,就不能再次改變

new Promise((resolve, reject) => {
  resolve('success')
  // 無(wú)效
  reject('reject')
})

當(dāng)我們?cè)跇?gòu)造 Promise 的時(shí)候据块,構(gòu)造函數(shù)內(nèi)部的代碼是立即執(zhí)行的

new Promise((resolve, reject) => {
  console.log('new Promise')
  resolve('success')
})
console.log('finifsh')
// new Promise -> finifsh

Promise 實(shí)現(xiàn)了鏈?zhǔn)秸{(diào)用,也就是說(shuō)每次調(diào)用 then 之后返回的都是一個(gè) Promise折剃,并且是一個(gè)全新的 Promise另假,原因也是因?yàn)闋顟B(tài)不可變。如果你在 then 中 使用了 return微驶,那么 return 的值會(huì)被 Promise.resolve() 包裝

Promise.resolve(1)
  .then(res => {
    console.log(res) // => 1
    return 2 // 包裝成 Promise.resolve(2)
  })
  .then(res => {
    console.log(res) // => 2
  })

當(dāng)然了浪谴,Promise 也很好地解決了回調(diào)地獄的問(wèn)題开睡,例如:

ajax(url, () => {
    // 處理邏輯
    ajax(url1, () => {
        // 處理邏輯
        ajax(url2, () => {
            // 處理邏輯
        })
    })
})

可以改寫(xiě)成:

ajax(url)
  .then(res => {
      console.log(res)
      return ajax(url1)
  }).then(res => {
      console.log(res)
      return ajax(url2)
  }).then(res => console.log(res))

for...of

for...of語(yǔ)句在可迭代對(duì)象(包括 Array因苹,Map,Set篇恒,String扶檐,TypedArray,arguments 對(duì)象等等)上創(chuàng)建一個(gè)迭代循環(huán)胁艰,調(diào)用自定義迭代鉤子款筑,并為每個(gè)不同屬性的值執(zhí)行語(yǔ)句。

例子如下:

const array1 = ['a', 'b', 'c'];

for (const element of array1) {
      console.log(element)
}

// "a"
// "b"
// "c"

Symbol

symbol 是一種基本數(shù)據(jù)類(lèi)型腾么,Symbol()函數(shù)會(huì)返回symbol類(lèi)型的值奈梳,該類(lèi)型具有靜態(tài)屬性和靜態(tài)方法。它的靜態(tài)屬性會(huì)暴露幾個(gè)內(nèi)建的成員對(duì)象解虱;它的靜態(tài)方法會(huì)暴露全局的symbol注冊(cè)攘须,且類(lèi)似于內(nèi)建對(duì)象類(lèi),但作為構(gòu)造函數(shù)來(lái)說(shuō)它并不完整殴泰,因?yàn)樗恢С终Z(yǔ)法:"new Symbol()"于宙。

每個(gè)從Symbol()返回的symbol值都是唯一的浮驳。一個(gè)symbol值能作為對(duì)象屬性的標(biāo)識(shí)符;這是該數(shù)據(jù)類(lèi)型僅有的目的捞魁。

例子如下:

const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo');

console.log(typeof symbol1); // "symbol"
console.log(symbol3.toString()); // "Symbol(foo)"
console.log(Symbol('foo') === Symbol('foo')); // false

迭代器(Iterator)/ 生成器(Generator)

迭代器(Iterator)是一種迭代的機(jī)制至会,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問(wèn)機(jī)制。任何數(shù)據(jù)結(jié)構(gòu)只要內(nèi)部有 Iterator 接口谱俭,就可以完成依次迭代操作奉件。

一旦創(chuàng)建,迭代器對(duì)象可以通過(guò)重復(fù)調(diào)用next()顯式地迭代旺上,從而獲取該對(duì)象每一級(jí)的值瓶蚂,直到迭代完,返回{ value: undefined, done: true }

雖然自定義的迭代器是一個(gè)有用的工具宣吱,但由于需要顯式地維護(hù)其內(nèi)部狀態(tài)窃这,因此需要謹(jǐn)慎地創(chuàng)建。生成器函數(shù)提供了一個(gè)強(qiáng)大的選擇:它允許你定義一個(gè)包含自有迭代算法的函數(shù)征候, 同時(shí)它可以自動(dòng)維護(hù)自己的狀態(tài)杭攻。生成器函數(shù)使用 function*[2]語(yǔ)法編寫(xiě)。最初調(diào)用時(shí)疤坝,生成器函數(shù)不執(zhí)行任何代碼兆解,而是返回一種稱(chēng)為Generator的迭代器。通過(guò)調(diào)用生成器的下一個(gè)方法消耗值時(shí)跑揉,Generator函數(shù)將執(zhí)行锅睛,直到遇到y(tǒng)ield關(guān)鍵字。

可以根據(jù)需要多次調(diào)用該函數(shù)历谍,并且每次都返回一個(gè)新的Generator现拒,但每個(gè)Generator只能迭代一次。

所以我們可以有以下例子:

function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
    for (let i = start; i < end; i += step) {
        yield i;
    }
}
var a = makeRangeIterator(1,10,2)
a.next() // {value: 1, done: false}
a.next() // {value: 3, done: false}
a.next() // {value: 5, done: false}
a.next() // {value: 7, done: false}
a.next() // {value: 9, done: false}
a.next() // {value: undefined, done: true}

Set/WeakSet

Set 對(duì)象允許你存儲(chǔ)任何類(lèi)型的唯一值望侈,無(wú)論是原始值或者是對(duì)象引用印蔬。

所以我們可以通過(guò)Set實(shí)現(xiàn)數(shù)組去重

const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)]) 
// [2, 3, 4, 5, 6, 7, 32]

WeakSet 結(jié)構(gòu)與 Set 類(lèi)似,但區(qū)別有以下兩點(diǎn):

?WeakSet 對(duì)象中只能存放對(duì)象引用, 不能存放值, 而 Set 對(duì)象都可以脱衙。?WeakSet 對(duì)象中存儲(chǔ)的對(duì)象值都是被弱引用的, 如果沒(méi)有其他的變量或?qū)傩砸眠@個(gè)對(duì)象值, 則這個(gè)對(duì)象值會(huì)被當(dāng)成垃圾回收掉. 正因?yàn)檫@樣, WeakSet 對(duì)象是無(wú)法被枚舉的, 沒(méi)有辦法拿到它包含的所有元素侥猬。

所以代碼如下:

var ws = new WeakSet()
var obj = {}
var foo = {}

ws.add(window)
ws.add(obj)

ws.has(window) // true
ws.has(foo)    // false, 對(duì)象 foo 并沒(méi)有被添加進(jìn) ws 中 

ws.delete(window) // 從集合中刪除 window 對(duì)象
ws.has(window)    // false, window 對(duì)象已經(jīng)被刪除了

ws.clear() // 清空整個(gè) WeakSet 對(duì)象

Map/WeakMap

Map 對(duì)象保存鍵值對(duì)。任何值(對(duì)象或者原始值) 都可以作為一個(gè)鍵或一個(gè)值捐韩。

例子如下退唠,我們甚至可以使用NaN來(lái)作為鍵值:

var myMap = new Map();
myMap.set(NaN, "not a number");

myMap.get(NaN); // "not a number"

var otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"

WeakMap 對(duì)象是一組鍵/值對(duì)的集合,其中的鍵是弱引用的荤胁。其鍵必須是對(duì)象瞧预,而值可以是任意的。

Map的區(qū)別與SetWeakSet的區(qū)別相似,具體代碼如下:

var wm1 = new WeakMap(),
    wm2 = new WeakMap(),
    wm3 = new WeakMap();
var o1 = {},
    o2 = function(){},
    o3 = window;

wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // value可以是任意值,包括一個(gè)對(duì)象
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // 鍵和值可以是任意對(duì)象,甚至另外一個(gè)WeakMap對(duì)象
wm1.get(o2); // "azerty"
wm2.get(o2); // undefined,wm2中沒(méi)有o2這個(gè)鍵
wm2.get(o3); // undefined,值就是undefined

wm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (即使值是undefined)

wm3.set(o1, 37);
wm3.get(o1); // 37
wm3.clear();
wm3.get(o1); // undefined,wm3已被清空
wm1.has(o1);   // true
wm1.delete(o1);
wm1.has(o1);   // false

Proxy/Reflect

Proxy 對(duì)象用于定義基本操作的自定義行為(如屬性查找松蒜,賦值扔茅,枚舉,函數(shù)調(diào)用等)秸苗。

Reflect 是一個(gè)內(nèi)置的對(duì)象召娜,它提供攔截 JavaScript 操作的方法。這些方法與 Proxy 的方法相同惊楼。Reflect不是一個(gè)函數(shù)對(duì)象玖瘸,因此它是不可構(gòu)造的。

ProxyReflect是非常完美的配合檀咙,例子如下:

const observe = (data, callback) => {
      return new Proxy(data, {
            get(target, key) {
                return Reflect.get(target, key)
            },
            set(target, key, value, proxy) {
                  callback(key, value);
                  target[key] = value;
                    return Reflect.set(target, key, value, proxy)
            }
      })
}

const FooBar = { open: false };
const FooBarObserver = observe(FooBar, (property, value) => {
  property === 'open' && value 
          ? console.log('FooBar is open!!!') 
          : console.log('keep waiting');
});
console.log(FooBarObserver.open) // false
FooBarObserver.open = true // FooBar is open!!!

當(dāng)然也不是什么都可以被代理的雅倒,如果對(duì)象帶有configurable: falsewritable: false 屬性,則代理失效弧可。

Regex對(duì)象的擴(kuò)展

正則新增符號(hào)

?i 修飾符

 // i 修飾符
  /[a-z]/i.test('\u212A') // false
  /[a-z]/iu.test('\u212A') // true

?y修飾符

// y修飾符
  var s = 'aaa_aa_a';
  var r1 = /a+/g;
  var r2 = /a+/y;

  r1.exec(s) // ["aaa"]
  r2.exec(s) // ["aaa"]

  r1.exec(s) // ["aa"]
  r2.exec(s) // null

?String.prototype.flags

  // 查看RegExp構(gòu)造函數(shù)的修飾符
  var regex = new RegExp('xyz', 'i')
  regex.flags // 'i'

?unicode模式

  var s = '??'
  /^.$/.test(s) // false
  /^.$/u.test(s) // true

?u轉(zhuǎn)義

 // u轉(zhuǎn)義
  /\,/ // /\,/
  /\,/u // 報(bào)錯(cuò) 沒(méi)有u修飾符時(shí)蔑匣,逗號(hào)前面的反斜杠是無(wú)效的,加了u修飾符就報(bào)錯(cuò)棕诵。

?引用

  const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
  RE_TWICE.test('abc!abc') // true
  RE_TWICE.test('abc!ab') // false

  const RE_TWICE = /^(?<word>[a-z]+)!\1$/;
  RE_TWICE.test('abc!abc') // true
  RE_TWICE.test('abc!ab') // false

字符串方法的實(shí)現(xiàn)改為調(diào)用RegExp方法

?String.prototype.match 調(diào)用 RegExp.prototype[Symbol.match]?String.prototype.replace 調(diào)用 RegExp.prototype[Symbol.replace]?String.prototype.search 調(diào)用 RegExp.prototype[Symbol.search]?String.prototype.split 調(diào)用 RegExp.prototype[Symbol.split]

正則新增屬性

?RegExp.prototype.sticky 表示是否有y修飾符

  /hello\d/y.sticky // true

?RegExp.prototype.flags獲取修飾符

  /abc/ig.flags // 'gi'

Math對(duì)象的擴(kuò)展

?二進(jìn)制表示法 : 0b或0B開(kāi)頭表示二進(jìn)制(0bXX0BXX)

?二進(jìn)制表示法 : 0b或0B開(kāi)頭表示二進(jìn)制(0bXX0BXX)

?八進(jìn)制表示法 : 0o或0O開(kāi)頭表示二進(jìn)制(0oXX0OXX)

?Number.EPSILON : 數(shù)值最小精度

?Number.MIN_SAFE_INTEGER : 最小安全數(shù)值(-2^53)

?Number.MAX_SAFE_INTEGER : 最大安全數(shù)值(2^53)

?Number.parseInt() : 返回轉(zhuǎn)換值的整數(shù)部分

?Number.parseFloat() : 返回轉(zhuǎn)換值的浮點(diǎn)數(shù)部分

?Number.isFinite() : 是否為有限數(shù)值

?Number.isNaN() : 是否為NaN

?Number.isInteger() : 是否為整數(shù)

?Number.isSafeInteger() : 是否在數(shù)值安全范圍內(nèi)

?Math.trunc() : 返回?cái)?shù)值整數(shù)部分

?Math.sign() : 返回?cái)?shù)值類(lèi)型(正數(shù)1裁良、負(fù)數(shù)-1零0)

?Math.cbrt() : 返回?cái)?shù)值立方根

?Math.clz32() : 返回?cái)?shù)值的32位無(wú)符號(hào)整數(shù)形式

?Math.imul() : 返回兩個(gè)數(shù)值相乘

?Math.fround() : 返回?cái)?shù)值的32位單精度浮點(diǎn)數(shù)形式

?Math.hypot() : 返回所有數(shù)值平方和的平方根

?Math.expm1() : 返回e^n - 1

?Math.log1p() : 返回1 + n的自然對(duì)數(shù)(Math.log(1 + n))

?Math.log10() : 返回以10為底的n的對(duì)數(shù)

?Math.log2() : 返回以2為底的n的對(duì)數(shù)

?Math.sinh() : 返回n的雙曲正弦

?Math.cosh() : 返回n的雙曲余弦

?Math.tanh() : 返回n的雙曲正切

?Math.asinh() : 返回n的反雙曲正弦

?Math.acosh() : 返回n的反雙曲余弦

?Math.atanh() : 返回n的反雙曲正切

Array對(duì)象的擴(kuò)展

?Array.prototype.from:轉(zhuǎn)換具有Iterator接口的數(shù)據(jù)結(jié)構(gòu)為真正數(shù)組校套,返回新數(shù)組价脾。

  console.log(Array.from('foo')) // ["f", "o", "o"]
  console.log(Array.from([1, 2, 3], x => x + x)) // [2, 4, 6]

?Array.prototype.of():轉(zhuǎn)換一組值為真正數(shù)組,返回新數(shù)組笛匙。

  Array.of(7)       // [7] 
  Array.of(1, 2, 3) // [1, 2, 3]

  Array(7)          // [empty, empty, empty, empty, empty, empty]
  Array(1, 2, 3)    // [1, 2, 3]

?Array.prototype.copyWithin():把指定位置的成員復(fù)制到其他位置侨把,返回原數(shù)組

  const array1 = ['a', 'b', 'c', 'd', 'e']

  console.log(array1.copyWithin(0, 3, 4)) // ["d", "b", "c", "d", "e"]

  console.log(array1.copyWithin(1, 3)) // ["d", "d", "e", "d", "e"]

?Array.prototype.find():返回第一個(gè)符合條件的成員

  const array1 = [5, 12, 8, 130, 44]

  const found = array1.find(element => element > 10)

  console.log(found) // 12

?Array.prototype.findIndex():返回第一個(gè)符合條件的成員索引值

  const array1 = [5, 12, 8, 130, 44]

  const isLargeNumber = (element) => element > 13

  console.log(array1.findIndex(isLargeNumber)) // 3

?Array.prototype.fill():根據(jù)指定值填充整個(gè)數(shù)組,返回原數(shù)組

  const array1 = [1, 2, 3, 4]

  console.log(array1.fill(0, 2, 4)) // [1, 2, 0, 0]

  console.log(array1.fill(5, 1)) // [1, 5, 5, 5]

  console.log(array1.fill(6)) // [6, 6, 6, 6]

?Array.prototype.keys():返回以索引值為遍歷器的對(duì)象

  const array1 = ['a', 'b', 'c']
  const iterator = array1.keys()

  for (const key of iterator) {
        console.log(key)
  }

  // 0
  // 1
  // 2

?Array.prototype.values():返回以屬性值為遍歷器的對(duì)象

  const array1 = ['a', 'b', 'c']
  const iterator = array1.values()

  for (const key of iterator) {
        console.log(key)
  }

  // a
  // b
  // c

?Array.prototype.entries():返回以索引值和屬性值為遍歷器的對(duì)象

  const array1 = ['a', 'b', 'c']
  const iterator = array1.entries()

  console.log(iterator.next().value) // [0, "a"]
  console.log(iterator.next().value) // [1, "b"]

?數(shù)組空位:ES6明確將數(shù)組空位轉(zhuǎn)為undefined或者empty

  Array.from(['a',,'b']) // [ "a", undefined, "b" ]
  [...['a',,'b']] // [ "a", undefined, "b" ]
  Array(3) //  [empty × 3]
  [,'a'] // [empty, "a"]

ES7(ES2016)

Array.prototype.includes()

includes() 方法用來(lái)判斷一個(gè)數(shù)組是否包含一個(gè)指定的值妹孙,根據(jù)情況秋柄,如果包含則返回 true,否則返回false涕蜂。

代碼如下:

const array1 = [1, 2, 3]
console.log(array1.includes(2)) // true

const pets = ['cat', 'dog', 'bat']
console.log(pets.includes('cat')) // true
console.log(pets.includes('at')) // false

冪運(yùn)算符**

冪運(yùn)算符**华匾,具有與Math.pow()一樣的功能映琳,代碼如下:

console.log(2**10) // 1024
console.log(Math.pow(2, 10)) // 1024

模板字符串(Template string)

自ES7起机隙,帶標(biāo)簽的模版字面量遵守以下轉(zhuǎn)義序列的規(guī)則:

?Unicode字符以"\u"開(kāi)頭,例如\u00A9?Unicode碼位用"\u{}"表示萨西,例如\u{2F804}?十六進(jìn)制以"\x"開(kāi)頭有鹿,例如\xA9?八進(jìn)制以""和數(shù)字開(kāi)頭,例如\251

這表示類(lèi)似下面這種帶標(biāo)簽的模版是有問(wèn)題的谎脯,因?yàn)閷?duì)于每一個(gè)ECMAScript語(yǔ)法葱跋,解析器都會(huì)去查找有效的轉(zhuǎn)義序列,但是只能得到這是一個(gè)形式錯(cuò)誤的語(yǔ)法:

latex`\unicode`
// 在較老的ECMAScript版本中報(bào)錯(cuò)(ES2016及更早)
// SyntaxError: malformed Unicode character escape sequence

ES8(ES2017)

async/await

雖然Promise可以解決回調(diào)地獄的問(wèn)題,但是鏈?zhǔn)秸{(diào)用太多娱俺,則會(huì)變成另一種形式的回調(diào)地獄 —— 面條地獄稍味,所以在ES8里則出現(xiàn)了Promise的語(yǔ)法糖async/await,專(zhuān)門(mén)解決這個(gè)問(wèn)題荠卷。

我們先看一下下面的Promise代碼:

fetch('coffee.jpg')
    .then(response => response.blob())
    .then(myBlob => {
          let objectURL = URL.createObjectURL(myBlob)
          let image = document.createElement('img')
          image.src = objectURL
          document.body.appendChild(image)
    })
    .catch(e => {
          console.log('There has been a problem with your fetch operation: ' + e.message)
    })

然后再看看async/await版的模庐,這樣看起來(lái)是不是更清晰了。

async function myFetch() {
      let response = await fetch('coffee.jpg')
      let myBlob = await response.blob()

      let objectURL = URL.createObjectURL(myBlob)
      let image = document.createElement('img')
      image.src = objectURL
      document.body.appendChild(image)
}

myFetch()

當(dāng)然油宜,如果你喜歡掂碱,你甚至可以?xún)烧呋煊?/p>

async function myFetch() {
      let response = await fetch('coffee.jpg')
      return await response.blob()
}

myFetch().then((blob) => {
      let objectURL = URL.createObjectURL(blob)
      let image = document.createElement('img')
      image.src = objectURL
      document.body.appendChild(image)
})

Object.values()

Object.values()方法返回一個(gè)給定對(duì)象自身的所有可枚舉屬性值的數(shù)組,值的順序與使用for...in循環(huán)的順序相同 ( 區(qū)別在于 for-in 循環(huán)枚舉原型鏈中的屬性 )慎冤。

代碼如下:

const object1 = {
      a: 'somestring',
      b: 42,
      c: false
}
console.log(Object.values(object1)) // ["somestring", 42, false]

Object.entries()

Object.entries()方法返回一個(gè)給定對(duì)象自身可枚舉屬性的鍵值對(duì)數(shù)組疼燥,其排列與使用 for...in 循環(huán)遍歷該對(duì)象時(shí)返回的順序一致(區(qū)別在于 for-in 循環(huán)還會(huì)枚舉原型鏈中的屬性)。

代碼如下:

const object1 = {
      a: 'somestring',
      b: 42
}

for (let [key, value] of Object.entries(object1)) {
      console.log(`${key}: ${value}`)
}

// "a: somestring"
// "b: 42"

padStart()

padStart() 方法用另一個(gè)字符串填充當(dāng)前字符串(重復(fù)蚁堤,如果需要的話)醉者,以便產(chǎn)生的字符串達(dá)到給定的長(zhǎng)度。填充從當(dāng)前字符串的開(kāi)始(左側(cè))應(yīng)用的披诗。

代碼如下:

const str1 = '5'
console.log(str1.padStart(2, '0')) // "05"

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

padEnd()

padEnd() 方法會(huì)用一個(gè)字符串填充當(dāng)前字符串(如果需要的話則重復(fù)填充)湃交,返回填充后達(dá)到指定長(zhǎng)度的字符串。從當(dāng)前字符串的末尾(右側(cè))開(kāi)始填充藤巢。

const str1 = 'Breaded Mushrooms'
console.log(str1.padEnd(25, '.')) // "Breaded Mushrooms........"
const str2 = '200'
console.log(str2.padEnd(5)) // "200  "

函數(shù)參數(shù)結(jié)尾逗號(hào)(Function parameter lists and calls trailing commas)

在ES5里就添加了對(duì)象的尾逗號(hào)舞肆,不過(guò)并不支持函數(shù)參數(shù),但是在ES8之后啸如,便開(kāi)始支持這一特性缘挑,代碼如下:

// 參數(shù)定義
function f(p) {}
function f(p,) {} 

(p) => {}
(p,) => {}

class C {
  one(a,) {},
  two(a, b,) {},
}

var obj = {
  one(a,) {},
  two(a, b,) {},
};

// 函數(shù)調(diào)用
f(p)
f(p,)

Math.max(10, 20)
Math.max(10, 20,)

但是以下的方式是不合法的:

僅僅包含逗號(hào)的函數(shù)參數(shù)定義或者函數(shù)調(diào)用會(huì)拋出 SyntaxError。而且绍刮,當(dāng)使用剩余參數(shù)的時(shí)候温圆,并不支持尾后逗號(hào),例子如下:

function f(,) {} // SyntaxError: missing formal parameter
(,) => {}       // SyntaxError: expected expression, got ','
f(,)             // SyntaxError: expected expression, got ','

function f(...p,) {} // SyntaxError: parameter after rest parameter
(...p,) => {}        // SyntaxError: expected closing parenthesis, got ','

在解構(gòu)里也可以使用孩革,代碼如下:

// 帶有尾后逗號(hào)的數(shù)組解構(gòu)
[a, b,] = [1, 2]

// 帶有尾后逗號(hào)的對(duì)象解構(gòu)
var o = {
  p: 42, 
  q: true,
}
var {p, q,} = o

同樣地岁歉,在使用剩余參數(shù)時(shí),會(huì)拋出 SyntaxError膝蜈,代碼如下:

var [a, ...b,] = [1, 2, 3] // SyntaxError: rest element may not have a trailing comma

ShareArrayBuffer(因安全問(wèn)題锅移,暫時(shí)在Chrome跟FireFox中被禁用)

SharedArrayBuffer 對(duì)象用來(lái)表示一個(gè)通用的,固定長(zhǎng)度的原始二進(jìn)制數(shù)據(jù)緩沖區(qū)饱搏,類(lèi)似于 ArrayBuffer 對(duì)象非剃,它們都可以用來(lái)在共享內(nèi)存(shared memory)上創(chuàng)建視圖。與 ArrayBuffer 不同的是推沸,SharedArrayBuffer 不能被分離备绽。

代碼如下:

et sab = new SharedArrayBuffer(1024) // 必須實(shí)例化
worker.postMessage(sab)

Atomics對(duì)象

Atomics對(duì)象 提供了一組靜態(tài)方法用來(lái)對(duì) SharedArrayBuffer[3] 對(duì)象進(jìn)行原子操作券坞。

方法如下:

?Atomics.add() :將指定位置上的數(shù)組元素與給定的值相加,并返回相加前該元素的值肺素。

?Atomics.and():將指定位置上的數(shù)組元素與給定的值相與恨锚,并返回與操作前該元素的值。

?Atomics.compareExchange():如果數(shù)組中指定的元素與給定的值相等倍靡,則將其更新為新的值眠冈,并返回該元素原先的值。

?Atomics.exchange():將數(shù)組中指定的元素更新為給定的值菌瘫,并返回該元素更新前的值蜗顽。

?Atomics.load():返回?cái)?shù)組中指定元素的值。

?Atomics.or():將指定位置上的數(shù)組元素與給定的值相或雨让,并返回或操作前該元素的值雇盖。

?Atomics.store():將數(shù)組中指定的元素設(shè)置為給定的值,并返回該值栖忠。

?Atomics.sub():將指定位置上的數(shù)組元素與給定的值相減崔挖,并返回相減前該元素的值。

?Atomics.xor():將指定位置上的數(shù)組元素與給定的值相異或庵寞,并返回異或操作前該元素的值狸相。

?Atomics.wait():檢測(cè)數(shù)組中某個(gè)指定位置上的值是否仍然是給定值,是則保持掛起直到被喚醒或超時(shí)捐川。返回值為 "ok"脓鹃、"not-equal" 或 "time-out"。調(diào)用時(shí)古沥,如果當(dāng)前線程不允許阻塞瘸右,則會(huì)拋出異常(大多數(shù)瀏覽器都不允許在主線程中調(diào)用 wait())。

?Atomics.wake():?jiǎn)拘训却?duì)列中正在數(shù)組指定位置的元素上等待的線程岩齿。返回值為成功喚醒的線程數(shù)量太颤。

?Atomics.isLockFree(size):可以用來(lái)檢測(cè)當(dāng)前系統(tǒng)是否支持硬件級(jí)的原子操作。對(duì)于指定大小的數(shù)組盹沈,如果當(dāng)前系統(tǒng)支持硬件級(jí)的原子操作龄章,則返回 true;否則就意味著對(duì)于該數(shù)組乞封,Atomics 對(duì)象中的各原子操作都只能用鎖來(lái)實(shí)現(xiàn)做裙。此函數(shù)面向的是技術(shù)專(zhuān)家。

Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptors() 方法用來(lái)獲取一個(gè)對(duì)象的所有自身屬性的描述符歌亲。代碼如下:

const object1 = {
  property1: 42
}

const descriptors1 = Object.getOwnPropertyDescriptors(object1)

console.log(descriptors1.property1.writable) // true

console.log(descriptors1.property1.value) // 42

// 淺拷貝一個(gè)對(duì)象
Object.create(
  Object.getPrototypeOf(obj), 
  Object.getOwnPropertyDescriptors(obj) 
)

// 創(chuàng)建子類(lèi)
function superclass() {}
superclass.prototype = {
  // 在這里定義方法和屬性
}
function subclass() {}
subclass.prototype = Object.create(superclass.prototype, Object.getOwnPropertyDescriptors({
  // 在這里定義方法和屬性
}))

ES9(ES2018)

for await...of

for await...of 語(yǔ)句會(huì)在異步或者同步可迭代對(duì)象上創(chuàng)建一個(gè)迭代循環(huán)菇用,包括 String澜驮,Array陷揪,Array-like 對(duì)象(比如arguments 或者NodeList),TypedArrayMap悍缠, Set和自定義的異步或者同步可迭代對(duì)象卦绣。其會(huì)調(diào)用自定義迭代鉤子,并為每個(gè)不同屬性的值執(zhí)行語(yǔ)句飞蚓。

配合迭代異步生成器滤港,例子如下:

async function* asyncGenerator() {
      var i = 0
      while (i < 3) {
            yield i++
      }
}

(async function() {
      for await (num of asyncGenerator()) {
            console.log(num)
      }
})()
// 0
// 1
// 2

模板字符串(Template string)

ES9開(kāi)始,模板字符串允許嵌套支持常見(jiàn)轉(zhuǎn)義序列趴拧,移除對(duì)ECMAScript在帶標(biāo)簽的模版字符串中轉(zhuǎn)義序列的語(yǔ)法限制溅漾。

不過(guò),非法轉(zhuǎn)義序列在"cooked"當(dāng)中仍然會(huì)體現(xiàn)出來(lái)著榴。它們將以undefined元素的形式存在于"cooked"之中添履,代碼如下:

function latex(str) { 
 return { "cooked": str[0], "raw": str.raw[0] }
} 

latex`\unicode` // { cooked: undefined, raw: "\\unicode" }

正則表達(dá)式反向(lookbehind)斷言

首先我們得先知道什么是斷言(Assertion)

斷言(Assertion)是一個(gè)對(duì)當(dāng)前匹配位置之前或之后的字符的測(cè)試脑又, 它不會(huì)實(shí)際消耗任何字符暮胧,所以斷言也被稱(chēng)為“非消耗性匹配”或“非獲取匹配”。

正則表達(dá)式的斷言一共有 4 種形式:

?(?=pattern) 零寬正向肯定斷言(zero-width positive lookahead assertion)?(?!pattern) 零寬正向否定斷言(zero-width negative lookahead assertion)?(?<=pattern) 零寬反向肯定斷言(zero-width positive lookbehind assertion)?(?<!pattern) 零寬反向否定斷言(zero-width negative lookbehind assertion)

在ES9之前问麸,JavaScript 正則表達(dá)式往衷,只支持正向斷言。正向斷言的意思是:當(dāng)前位置后面的字符串應(yīng)該滿(mǎn)足斷言严卖,但是并不捕獲席舍。例子如下:

'fishHeadfishTail'.match(/fish(?=Head)/g) // ["fish"]

反向斷言和正向斷言的行為一樣,只是方向相反哮笆。例子如下:

'abc123'.match(/(?<=(\d+)(\d+))$/) //  ["", "1", "23", index: 6, input: "abc123", groups: undefined]

正則表達(dá)式 Unicode 轉(zhuǎn)義

正則表達(dá)式中的Unicode轉(zhuǎn)義符允許根據(jù)Unicode字符屬性匹配Unicode字符俺亮。它允許區(qū)分字符類(lèi)型,例如大寫(xiě)和小寫(xiě)字母疟呐,數(shù)學(xué)符號(hào)和標(biāo)點(diǎn)符號(hào)脚曾。

部分例子代碼如下:

// 匹配所有數(shù)字
const regex = /^\p{Number}+$/u;
regex.test('231???') // true
regex.test('???') // true
regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ') // true

// 匹配所有空格
\p{White_Space}

// 匹配各種文字的所有字母,等同于 Unicode 版的 \w
[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]

// 匹配各種文字的所有非字母的字符启具,等同于 Unicode 版的 \W
[^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]

// 匹配 Emoji
/\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu

// 匹配所有的箭頭字符
const regexArrows = /^\p{Block=Arrows}+$/u;
regexArrows.test('←↑→↓??↖↗↘↙?????????????') // true

具體的屬性列表可查看:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes

正則表達(dá)式 s/dotAll 模式

在以往的版本里本讥,JS的正則的.只能匹配emoji跟行終結(jié)符以外的所有文本,例如:

let regex = /./;

regex.test('\n');       // false
regex.test('\r');       // false
regex.test('\u{2028}'); // false
regex.test('\u{2029}'); // false

regex.test('\v');       // true
regex.test('\f');       // true
regex.test('\u{0085}'); // true

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

/foo.bar/.test('foo\nbar');     // false
/foo[\s]bar/.test('foo\nbar');   // true

但是在ES9之后鲁冯,JS正則增加了一個(gè)新的標(biāo)志 s 用來(lái)表示 dotAll拷沸,這可以匹配任意字符。代碼如下:

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

const re = /foo.bar/s;  //  等價(jià)于 const re = new RegExp('foo.bar', 's');
re.test('foo\nbar');    // true
re.dotAll;      // true
re.flags;       // "s"

正則表達(dá)式命名捕獲組

在以往的版本里薯演,JS的正則分組是無(wú)法命名的撞芍,所以容易混淆。例如下面獲取年月日的例子跨扮,很容易讓人搞不清哪個(gè)是月份序无,哪個(gè)是年份:

const matched = /(\d{4})-(\d{2})-(\d{2})/.exec('2019-01-01')
console.log(matched[0]);    // 2019-01-01
console.log(matched[1]);    // 2019
console.log(matched[2]);    // 01
console.log(matched[3]);    // 01

ES9引入了命名捕獲組验毡,允許為每一個(gè)組匹配指定一個(gè)名字,既便于閱讀代碼帝嗡,又便于引用晶通。代碼如下:

const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31

const RE_OPT_A = /^(?<as>a+)?$/;
const matchObj = RE_OPT_A.exec('');

matchObj.groups.as // undefined
'as' in matchObj.groups // true

對(duì)象擴(kuò)展操作符

ES6中添加了數(shù)組的擴(kuò)展操作符,讓我們?cè)诓僮鲾?shù)組時(shí)更加簡(jiǎn)便哟玷,美中不足的是并不支持對(duì)象擴(kuò)展操作符狮辽,但是在ES9開(kāi)始,這一功能也得到了支持巢寡,例如:

var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };

var clonedObj = { ...obj1 };
// 克隆后的對(duì)象: { foo: "bar", x: 42 }

var mergedObj = { ...obj1, ...obj2 };
// 合并后的對(duì)象: { foo: "baz", x: 42, y: 13 }

上面便是一個(gè)簡(jiǎn)便的淺拷貝喉脖。這里有一點(diǎn)小提示,就是Object.assign() 函數(shù)會(huì)觸發(fā) setters抑月,而展開(kāi)語(yǔ)法則不會(huì)动看。所以不能替換也不能模擬Object.assign()

如果存在相同的屬性名爪幻,只有最后一個(gè)會(huì)生效菱皆。

Promise.prototype.finally()

finally()方法會(huì)返回一個(gè)Promise,當(dāng)promise的狀態(tài)變更挨稿,不管是變成rejected或者fulfilled仇轻,最終都會(huì)執(zhí)行finally()的回調(diào)。

例子如下:

fetch(url)
      .then((res) => {
        console.log(res)
      })
      .catch((error) => { 
        console.log(error)
      })
      .finally(() => { 
        console.log('結(jié)束')
    })

ES10(ES2019)

Array.prototype.flat() / flatMap()

flat() 方法會(huì)按照一個(gè)可指定的深度遞歸遍歷數(shù)組奶甘,并將所有元素與遍歷到的子數(shù)組中的元素合并為一個(gè)新數(shù)組返回篷店。

flatMap()map() 方法和深度為1的 flat() 幾乎相同.,不過(guò)它會(huì)首先使用映射函數(shù)映射每個(gè)元素臭家,然后將結(jié)果壓縮成一個(gè)新數(shù)組疲陕,這樣效率會(huì)更高。

例子如下:

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

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

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

// 深度為1
arr1.flatMap(x => [[x * 2]]) // [[2], [4], [6], [8]]

flatMap()可以代替reduce()concat()钉赁,例子如下:

var arr = [1, 2, 3, 4]
arr.flatMap(x => [x, x * 2]) // [1, 2, 2, 4, 3, 6, 4, 8]
// 等價(jià)于
arr.reduce((acc, x) => acc.concat([x, x * 2]), []) // [1, 2, 2, 4, 3, 6, 4, 8]

但這是非常低效的蹄殃,在每次迭代中,它創(chuàng)建一個(gè)必須被垃圾收集的新臨時(shí)數(shù)組你踩,并且它將元素從當(dāng)前的累加器數(shù)組復(fù)制到一個(gè)新的數(shù)組中诅岩,而不是將新的元素添加到現(xiàn)有的數(shù)組中。

String.prototype.trimStart() / trimLeft() / trimEnd() / trimRight()

在ES5中带膜,我們可以通過(guò)trim()來(lái)去掉字符首尾的空格吩谦,但是卻無(wú)法只去掉單邊的,但是在ES10之后膝藕,我們可以實(shí)現(xiàn)這個(gè)功能式廷。

如果我們要去掉開(kāi)頭的空格,可以使用trimStart()或者它的別名trimLeft()芭挽,

同樣的滑废,如果我們要去掉結(jié)尾的空格蝗肪,我們可以使用trimEnd()或者它的別名trimRight()

例子如下:

const Str = '   Hello world!  '
console.log(Str) // '   Hello world!  '
console.log(Str.trimStart()) // 'Hello world!  '
console.log(Str.trimLeft()) // 'Hello world!  '
console.log(Str.trimEnd()) // '   Hello world!'
console.log(Str.trimRight()) // '   Hello world!'

不過(guò)這里有一點(diǎn)要注意的是策严,trimStart()trimEnd()才是標(biāo)準(zhǔn)方法穗慕,trimLeft()trimRight()只是別名饿敲。

在某些引擎里(例如Chrome)妻导,有以下的等式:

String.prototype.trimLeft.name === "trimStart"

String.prototype.trimRight.name === "trimEnd"

Object.fromEntries()

Object.fromEntries() 方法把鍵值對(duì)列表轉(zhuǎn)換為一個(gè)對(duì)象,它是Object.entries()的反函數(shù)怀各。

例子如下:

const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
])

const obj = Object.fromEntries(entries)

console.log(obj) // Object { foo: "bar", baz: 42 }

Symbol.prototype.description

description 是一個(gè)只讀屬性倔韭,它會(huì)返回Symbol對(duì)象的可選描述的字符串。與 Symbol.prototype.toString() 不同的是它不會(huì)包含Symbol()的字符串瓢对。例子如下:

Symbol('desc').toString();   // "Symbol(desc)"
Symbol('desc').description;  // "desc"
Symbol('').description;      // ""
Symbol().description;        // undefined

// 具名 symbols
Symbol.iterator.toString();  // "Symbol(Symbol.iterator)"
Symbol.iterator.description; // "Symbol.iterator"

//全局 symbols
Symbol.for('foo').toString();  // "Symbol(foo)"
Symbol.for('foo').description; // "foo"

String.prototype.matchAll

matchAll() 方法返回一個(gè)包含所有匹配正則表達(dá)式的結(jié)果及分組捕獲組的迭代器寿酌。并且返回一個(gè)不可重啟的迭代器。例子如下:

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

str.match(regexp) // ['test1', 'test2']
str.matchAll(regexp) // RegExpStringIterator {}
[...str.matchAll(regexp)] // [['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4], ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4]]

Function.prototype.toString() 返回注釋與空格

在以往的版本中硕蛹,Function.prototype.toString()得到的字符串是去掉空白符號(hào)的醇疼,但是從ES10開(kāi)始會(huì)保留這些空格,如果是原生函數(shù)則返回你控制臺(tái)看到的效果法焰,例子如下:

function sum(a, b) {
      return a + b;
}

console.log(sum.toString())
// "function sum(a, b) {
//         return a + b;
//  }"

console.log(Math.abs.toString()) // "function abs() { [native code] }"

try-catch

在以往的版本中秧荆,try-catchcatch后面必須帶異常參數(shù),例如:

// ES10之前
try {
// tryCode
} catch (err) {
// catchCode
}

但是在ES10之后埃仪,這個(gè)參數(shù)卻不是必須的乙濒,如果用不到,我們可以不用傳卵蛉,例如:

try {
      console.log('Foobar')
} catch {
      console.error('Bar')
}

BigInt

BigInt 是一種內(nèi)置對(duì)象颁股,它提供了一種方法來(lái)表示大于 253 - 1 的整數(shù)。這原本是 Javascript中可以用 Number 表示的最大數(shù)字傻丝。BigInt 可以表示任意大的整數(shù)甘有。

可以用在一個(gè)整數(shù)字面量后面加 n 的方式定義一個(gè) BigInt ,如:10n葡缰,或者調(diào)用函數(shù)BigInt()梧疲。

在以往的版本中,我們有以下的弊端:

// 大于2的53次方的整數(shù)运准,無(wú)法保持精度
2 ** 53 === (2 ** 53 + 1)
// 超過(guò)2的1024次方的數(shù)值幌氮,無(wú)法表示
2 ** 1024 // Infinity

但是在ES10引入BigInt之后,這個(gè)問(wèn)題便得到了解決胁澳。

以下操作符可以和 BigInt 一起使用: +该互、*-韭畸、**宇智、% 蔓搞。除 >>> (無(wú)符號(hào)右移)之外的位操作也可以支持。因?yàn)?BigInt 都是有符號(hào)的随橘, >>> (無(wú)符號(hào)右移)不能用于 BigInt喂分。BigInt 不支持單目 (+) 運(yùn)算符。

/ 操作符對(duì)于整數(shù)的運(yùn)算也沒(méi)問(wèn)題机蔗∑哑恚可是因?yàn)檫@些變量是 BigInt 而不是 BigDecimal ,該操作符結(jié)果會(huì)向零取整萝嘁,也就是說(shuō)不會(huì)返回小數(shù)部分梆掸。

BigIntNumber不是嚴(yán)格相等的,但是寬松相等的牙言。

所以在BigInt出來(lái)以后酸钦,JS的原始類(lèi)型便增加到了7個(gè),如下:

?Boolean?Null?Undefined?Number?String?Symbol (ES6)?BigInt (ES10)

globalThis

globalThis屬性包含類(lèi)似于全局對(duì)象 this值咱枉。所以在全局環(huán)境下卑硫,我們有:

globalThis === this // true

import()

靜態(tài)的import 語(yǔ)句用于導(dǎo)入由另一個(gè)模塊導(dǎo)出的綁定。無(wú)論是否聲明了 嚴(yán)格模式蚕断,導(dǎo)入的模塊都運(yùn)行在嚴(yán)格模式下欢伏。在瀏覽器中,import 語(yǔ)句只能在聲明了 type="module"script 的標(biāo)簽中使用基括。

但是在ES10之后颜懊,我們有動(dòng)態(tài) import(),它不需要依賴(lài) type="module" 的script標(biāo)簽风皿。

所以我們有以下例子:

const main = document.querySelector("main")
for (const link of document.querySelectorAll("nav > a")) {
      link.addEventListener("click", e => {
            e.preventDefault()

            import('/modules/my-module.js')
              .then(module => {
                    module.loadPageInto(main);
              })
              .catch(err => {
                    main.textContent = err.message;
              })
      })
}

私有元素與方法

在ES10之前河爹,如果我們要實(shí)現(xiàn)一個(gè)簡(jiǎn)單的計(jì)數(shù)器組件,我們可能會(huì)這么寫(xiě):

// web component 寫(xiě)法
class Counter extends HTMLElement {
      get x() { 
              return this.xValue
      }
      set x(value) {
              this.xValue = value
              window.requestAnimationFrame(this.render.bind(this))
      }

      clicked() {
            this.x++
      }

      constructor() {
            super()
            this.onclick = this.clicked.bind(this)
            this.xValue = 0
      }

      connectedCallback() { 
              this.render()
      }

      render() {
            this.textContent = this.x.toString()
      }
}
window.customElements.define('num-counter', Counter)

但是在ES10之后我們可以使用私有變量進(jìn)行組件封裝桐款,如下:

class Counter extends HTMLElement {
      #xValue = 0

      get #x() { 
          return #xValue
      }
      set #x(value) {
            this.#xValue = value
            window.requestAnimationFrame(this.#render.bind(this))
      }

      #clicked() {
            this.#x++
      }

      constructor() {
            super();
            this.onclick = this.#clicked.bind(this)
      }

      connectedCallback() { 
              this.#render()
      }

      #render() {
            this.textContent = this.#x.toString()
      }
}
window.customElements.define('num-counter', Counter)

參考資料

1.ECMAScript 6 入門(mén)[4]
2.1.5萬(wàn)字概括ES6全部特性[5]
3.MDN[6]
4.ES2018 新特征之:非轉(zhuǎn)義序列的模板字符串[7]
5.正則表達(dá)式反向(lookbehind)斷言[8]
6.Unicode property escapes[9]
7.exnext提案[10]
8.ES7咸这、ES8、ES9魔眨、ES10新特性大盤(pán)點(diǎn)
9.Ecma TC39[11]
10.[ECMAScript] TC39 process[12]
11.The TC39 Process[13]

References

[1]KRISACHAN: https://github.com/KRISACHAN
[2] function*: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/function*
[3] SharedArrayBuffer: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
[4]ECMAScript 6 入門(mén): https://es6.ruanyifeng.com/#docs/
[5]1.5萬(wàn)字概括ES6全部特性: https://juejin.im/post/5d9bf530518825427b27639d
[6]MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript
[7]ES2018 新特征之:非轉(zhuǎn)義序列的模板字符串: https://segmentfault.com/a/1190000013519526
[8]正則表達(dá)式反向(lookbehind)斷言: https://esnext.justjavac.com/proposal/regexp-lookbehind.html
[9]Unicode property escapes: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes
[10]exnext提案: https://esnext.justjavac.com/proposal/exponentiation-operator.html
[11]Ecma TC39: https://github.com/tc39
[12][ECMAScript] TC39 process: http://www.reibang.com/p/b0877d1fc2a4
[13] The TC39 Process: https://tc39.es/process-document/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末媳维,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子遏暴,更是在濱河造成了極大的恐慌侄刽,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朋凉,死亡現(xiàn)場(chǎng)離奇詭異州丹,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)墓毒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)吓揪,“玉大人,你說(shuō)我怎么就攤上這事所计∧牵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵主胧,是天一觀的道長(zhǎng)叭首。 經(jīng)常有香客問(wèn)我,道長(zhǎng)讥裤,這世上最難降的妖魔是什么放棒? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任姻报,我火速辦了婚禮己英,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吴旋。我一直安慰自己损肛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布荣瑟。 她就那樣靜靜地躺著治拿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪笆焰。 梳的紋絲不亂的頭發(fā)上劫谅,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音嚷掠,去河邊找鬼捏检。 笑死,一個(gè)胖子當(dāng)著我的面吹牛不皆,可吹牛的內(nèi)容都是我干的贯城。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼霹娄,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼能犯!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起犬耻,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤踩晶,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后枕磁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體渡蜻,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年透典,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晴楔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顿苇。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖税弃,靈堂內(nèi)的尸體忽然破棺而出纪岁,到底是詐尸還是另有隱情,我是刑警寧澤则果,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布幔翰,位于F島的核電站,受9級(jí)特大地震影響西壮,放射性物質(zhì)發(fā)生泄漏遗增。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一款青、第九天 我趴在偏房一處隱蔽的房頂上張望做修。 院中可真熱鬧,春花似錦抡草、人聲如沸饰及。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)燎含。三九已至,卻和暖如春腿短,著一層夾襖步出監(jiān)牢的瞬間屏箍,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工橘忱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赴魁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓鹦付,卻偏偏與公主長(zhǎng)得像尚粘,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子敲长,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354