日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
ES6—ES14新特性一覽

一、ES6 新特性(2015)

1、let和const

在ES6中,新增了let和const關(guān)鍵字,其中 let 主要用來聲明變量,而 const 通常用來聲明常量。let、const相對于var關(guān)鍵字有以下特點(diǎn):

成都創(chuàng)新互聯(lián)長期為上千余家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為裕民企業(yè)提供專業(yè)的成都做網(wǎng)站、網(wǎng)站制作,裕民網(wǎng)站改版等技術(shù)服務(wù)。擁有10年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。

特性

var

let

const

變量提升

?

×

×

全局變量

?

×

×

重復(fù)聲明

?

×

×

重新賦值

?

?

×

暫時性死區(qū)

×

?

?

塊作用域

×

?

?

只聲明不初始化

?

?

×

這里主要介紹其中的四點(diǎn):

(1)重新賦值

const 關(guān)鍵字聲明的變量是“不可修改”的。其實(shí),const 保證的并不是變量的值不能改動,而是變量指向的那個內(nèi)存地址不能改動。對于基本類型的數(shù)據(jù)(數(shù)值、字符串、布爾值),其值就保存在變量指向的那個內(nèi)存地址,因此等同于常量。但對于引用類型的數(shù)據(jù)(主要是對象和數(shù)組),變量指向數(shù)據(jù)的內(nèi)存地址,保存的只是一個指針,const只能保證這個指針是不變的,至于它指向的數(shù)據(jù)結(jié)構(gòu)就不可控制了。

(2)塊級作用域

在引入let和const之前是不存在塊級作用域的說法的,這也就導(dǎo)致了很多問題,比如內(nèi)層變量會覆蓋外層的同名變量:

var a = 1;
if (true) {
  var a = 2;
}

console.log(a); // 輸出結(jié)果:2

循環(huán)變量會泄漏為全局變量:

var arr = [1, 2, 3];
for (var i = 0; i < arr.length; i++) {
  console.log(arr[i]);  // 輸出結(jié)果:1  2  3
}

console.log(i); // 輸出結(jié)果:3

而通過let和const定義的變量存在塊級作用域,就不會產(chǎn)生上述問題:

let a = 1;
if (true) {
  let a = 2;
}

console.log(a); // 輸出結(jié)果:1

const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);  // 輸出結(jié)果:1  2  3
}

console.log(i); // Uncaught ReferenceError: i is not defined

(3)變量提升

我們知道,在ES6之前是存在變量提升的,所謂的變量提升就是變量可以在聲明之前使用:

console.log(a); // 輸出結(jié)果:undefined
var a = 1;

變量提升的本質(zhì)是JavaScript引擎在執(zhí)行代碼之前會對代碼進(jìn)行編譯分析,這個階段會將檢測到的變量和函數(shù)聲明添加到 JavaScript 引擎中名為 Lexical Environment 的內(nèi)存中,并賦予一個初始化值 undefined。然后再進(jìn)入代碼執(zhí)行階段。所以在代碼執(zhí)行之前,JS 引擎就已經(jīng)知道聲明的變量和函數(shù)。

這種現(xiàn)象就不太符合我們的直覺,所以在ES6中,let和const關(guān)鍵字限制了變量提升,let 定義的變量添加到 Lexical Environment 后不再進(jìn)行初始化為 undefined 操作,JS 引擎只會在執(zhí)行到詞法聲明和賦值時才進(jìn)行初始化。而在變量創(chuàng)建到真正初始化之間的時間跨度內(nèi),它們無法訪問或使用,ES6 將其稱之為暫時性死區(qū):

// 暫時性死區(qū) 開始
a = "hello";     //  Uncaught ReferenceError: Cannot access 'a' before initialization

let a;   
//  暫時性死區(qū) 結(jié)束
console.log(a);  // undefined

(4)重復(fù)聲明

在ES6之前,var關(guān)鍵字聲明的變量對于一個作用域內(nèi)變量的重復(fù)聲明是沒有限制的,甚至可以聲明與參數(shù)同名變量,以下兩個函數(shù)都不會報(bào)錯:

function funcA() {
  var a = 1;
  var a = 2;
}

function funcB(args) {
  var args = 1; 
}

而let修復(fù)了這種不嚴(yán)謹(jǐn)?shù)脑O(shè)計(jì):

function funcA() {
  let a = 1;
  let a = 2;  // Uncaught SyntaxError: Identifier 'a' has already been declared
}

function funcB(args) {
  let args = 1;  // Uncaught SyntaxError: Identifier 'args' has already been declared
}

現(xiàn)在我們項(xiàng)目中已經(jīng)完全放棄了var,而使用let來定義變量,使用const來定義常量。在ESlint開啟了如下規(guī)則:

"no-var": 0;

2、解構(gòu)賦值

ES6中還引入了解構(gòu)賦值的概念,解構(gòu)賦值遵循“模式匹配”,即只要等號兩邊的模式相等,左邊的變量就會被賦予對應(yīng)的值。不同類型數(shù)據(jù)的解構(gòu)方式不同,下面就分別來看看不同類型數(shù)據(jù)的解構(gòu)方式。

平時在開發(fā)中,我主要會用到對象的解構(gòu)賦值,比如在React中解構(gòu)porps值等,使用解構(gòu)賦值來獲取父組件傳來的值;在React Hooks中的useState使用到了數(shù)組的解構(gòu)賦值;

(1)數(shù)組解構(gòu)

具有 Iterator 接口的數(shù)據(jù)結(jié)構(gòu),都可以采用數(shù)組形式的解構(gòu)賦值。

const [foo, [[bar], baz]] = [1, [[2], 3]];
console.log(foo, bar, baz) // 輸出結(jié)果:1  2  3

這里,ES6實(shí)現(xiàn)了對數(shù)組的結(jié)構(gòu),并依次賦值變量foo、bar、baz。數(shù)組的解構(gòu)賦值按照位置將值與變量對應(yīng)。

數(shù)組還可以實(shí)現(xiàn)不完全解構(gòu),只解構(gòu)部分內(nèi)容:

const [x, y] = [1, 2, 3];   // 提取前兩個值
const [, y, z] = [1, 2, 3]  // 提取后兩個值
const [x, , z] = [1, 2, 3]  // 提取第一三個值

如果解構(gòu)時對應(yīng)的位置沒有值就會將變量賦值為undefined:

const [x, y, z] = [1, 2]; 
console.log(z)  // 輸出結(jié)果:undefined

數(shù)組解構(gòu)賦值可以使用rest操作符來捕獲剩余項(xiàng):

const [x, ...y] = [1, 2, 3];   
console.log(x);  // 輸出結(jié)果:1
console.log(y);  // 輸出結(jié)果:[2, 3]

在解構(gòu)時還支持使用默認(rèn)值,當(dāng)對應(yīng)的值為undefined時才會使用默認(rèn)值:

const [x, y, z = 3] = [1, 2]; 
console.log(z)  // 輸出結(jié)果:3

(2)對象解構(gòu)

對象的解構(gòu)賦值的本質(zhì)其實(shí)是先找到同名的屬性,在賦值給對應(yīng)的變量:

let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
console.log(foo, bar); // 輸出結(jié)果:aaa  bbb

需要注意的是,在JavaScript中,對象的屬性是沒有順序的。所以,在解構(gòu)賦值時,變量必須與屬性同名才能去取到值。

對象的解構(gòu)賦值也是支持默認(rèn)值的,當(dāng)定義的變量在對象中不存在時,其默認(rèn)值才會生效:

let { foo, bar, baz = 'ccc'} = { foo: 'aaa', bar: 'bbb', baz: null };
console.log(foo, bar, baz); // 輸出結(jié)果:aaa  bbb  null

let { foo, bar, baz = 'ccc'} = { foo: 'aaa', bar: 'bbb' };
console.log(foo, bar, baz); // 輸出結(jié)果:aaa  bbb  ccc

可以看到,只有定義的變量是嚴(yán)格的===undefined時,它的默認(rèn)值才會生效。

除此之外,我們還需要注意,不能給已聲明的變量進(jìn)行賦值,因?yàn)楫?dāng)缺少 let、const、var 關(guān)鍵詞時,將會把 {baz} 理解為代碼塊從而導(dǎo)致語法錯誤,所以下面代碼會報(bào)錯:

let baz;
{ baz } = { foo: 'aaa', bar: 'bbb', baz: 'ccc' };

可以使用括號包裹整個解構(gòu)賦值語句來解決上述問題:

let baz;
({ baz } = { foo: 'aaa', bar: 'bbb', baz: 'ccc' });
console.log(baz)

在對象的解構(gòu)賦值中,可以將現(xiàn)有對象的方法賦值給某個變量,比如:

let { log, sin, cos } = Math;
log(12)  // 輸出結(jié)果:2.4849066497880004
sin(1)   // 輸出結(jié)果:0.8414709848078965
cos(1)   // 輸出結(jié)果:0.5403023058681398

(3)其他解構(gòu)賦值

剩下的幾種解構(gòu)賦值,目前我在項(xiàng)目中應(yīng)用的較少,來簡單看一下。

  • 字符串解構(gòu)

字符串解構(gòu)規(guī)則:只要等號右邊的值不是對象或數(shù)組,就先將其轉(zhuǎn)為類數(shù)組對象,在進(jìn)行解構(gòu):

const [a, b, c, d, e] = 'hello';
console.log(a, b, c, d, e)  // 輸出結(jié)果:h e l l o

類數(shù)組對象有 length 屬性,因此可以給這個屬性進(jìn)行解構(gòu)賦值:

let {length} = 'hello';    // 輸出結(jié)果:5

由于字符串都是一個常量,所以我們通常是知道它的值是什么的,所以很少會使用變量的解構(gòu)賦值。

  • 數(shù)值和布爾值解構(gòu)賦值

對數(shù)值和布爾值進(jìn)行解構(gòu)時,它們將會先被轉(zhuǎn)為對象,然后再應(yīng)用解構(gòu)語法:

let {toString: s} = 123;
s === Number.prototype.toString // 輸出結(jié)果:true

let {toString: s} = true;
s === Boolean.prototype.toString // 輸出結(jié)果:true

注意null和undefined不能轉(zhuǎn)換為對象,所以如果右邊是這兩個值,就會報(bào)錯。

  • 函數(shù)參數(shù)解構(gòu)賦值

函數(shù)參數(shù)表面上是一個數(shù)組,在傳入?yún)?shù)的那一刻,就會被解構(gòu)為x和y。

function add([x, y]){
  return x + y;
}
add([1, 2]);   // 3

除此之外,我們還可以解構(gòu)函數(shù)的返回值:

function example() {
  return [1, 2, 3];
}
let [a, b, c] = example();

3、模板字符串

傳統(tǒng)的JavaScript語言中,輸出模板經(jīng)常使用的是字符串拼接的形式,這樣寫相當(dāng)繁瑣,在ES6中引入了模板字符串的概念來解決以上問題。

模板字符串是增強(qiáng)版的字符串,用反引號``來標(biāo)識,他可以用來定義單行字符串,也可以定義多行字符串,或者在字符串中嵌入變量。

// 字符串中嵌入變量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

// 字符串中調(diào)用函數(shù)
` ${fn()}

在平時的開發(fā)中,除了上面代碼中的應(yīng)用,很多地方會用到模板字符串,比如拼接一個DOM串,在Emotion/styled中定義DOM結(jié)構(gòu)等,都會用到模板字符串。不過在模板字符串中定義DOM元素就不會有代碼提示了。

在使用模板字符串時,需要注意以下幾點(diǎn):

  • 如果在字符串中使用反引號,需要使用\來轉(zhuǎn)義;
  • 如果在多行字符串中有空格和縮進(jìn),那么它們都會被保留在輸出中;
  • 模板字符串中嵌入變量,需要將變量名寫在${}之中;
  • 模板字符串中可以放任意的表達(dá)式,也可以進(jìn)行運(yùn)算,以及引用對象的屬性,甚至可以調(diào)用函數(shù);
  • 如果模板字符中的變量沒有聲明,會報(bào)錯。

4、函數(shù)默認(rèn)參數(shù)

在ES6之前,函數(shù)是不支持默認(rèn)參數(shù)的,ES6實(shí)現(xiàn)了對此的支持,并且只有不傳入?yún)?shù)時才會觸發(fā)默認(rèn)值:

function getPoint(x = 0, y = 0) {
  console.log(x, y);
}

getPoint(1, 2);   // 1  2
getPoint()        // 0  0 
getPoint(1)       // 1  0

當(dāng)使用函數(shù)默認(rèn)值時,需要注意以下幾點(diǎn):

(1)函數(shù)length屬性值

函數(shù)length屬性通常用來表示函數(shù)參數(shù)的個數(shù),當(dāng)引入函數(shù)默認(rèn)值之后,length表示的就是第一個有默認(rèn)值參數(shù)之前的普通參數(shù)個數(shù):

const funcA = function(x, y) {};
console.log(funcA.length);  // 輸出結(jié)果:2 

const funcB = function(x, y = 1) {};
console.log(funcB.length);  // 輸出結(jié)果:1

const funcC = function(x = 1, y) {};
console.log(funcC.length);  // 輸出結(jié)果 0

(2)參數(shù)作用域

當(dāng)給函數(shù)的參數(shù)設(shè)置了默認(rèn)值之后,參數(shù)在被初始化時將形成一個獨(dú)立作用域,初始化完成后作用域消解:

let x = 1;

function func(x, y = x) {
  console.log(y);
}

func(2);

這里最終會打印出2。在函數(shù)調(diào)用時,參數(shù) x, y 將形成一個獨(dú)立的作用域,所以參數(shù)中的y會等于第一個參數(shù)中的x,而不是上面定義的1。

5、箭頭函數(shù)

ES6中引入了箭頭函數(shù),用來簡化函數(shù)的定義:

const counter = (x, y) => x + y;

相對于普通函數(shù),箭頭函數(shù)有以下特點(diǎn):

(1)更加簡潔

  • 如果沒有參數(shù),就直接寫一個空括號即可
  • 如果只有一個參數(shù),可以省去參數(shù)的括號
  • 如果有多個參數(shù),用逗號分割
  • 如果函數(shù)體的返回值只有一句,可以省略大括號
// 1. 不傳入?yún)?shù)
const funcA = () => console.log('funcA');
// 等價于
const funcA = function() {
  console.log('funcA');
} 

// 2. 傳入?yún)?shù)
const funcB = (x, y) => x + y;
// 等價于
const funcB = function(x, y) {
  return x + y;
} 

// 3. 單個參數(shù)的簡化
const funcC = (x) => x;
// 對于單個參數(shù),可以去掉 (),簡化為
const funcC = x => x;
// 等價于
const funcC = function(x) {
  return x;
}

// 4. 上述代碼函數(shù)體只有單條語句,如果有多條,需要使用 {}
const funcD = (x, y) => { console.log(x, y); return x + y; }
// 等價于
const funcD = function(x, y) {
  console.log(x, y);
  return x + y;
}

(2)不綁定 this

箭頭函數(shù)不會創(chuàng)建自己的this, 所以它沒有自己的this,它只會在自己作用域的上一層繼承this。所以箭頭函數(shù)中this的指向在它在定義時已經(jīng)確定了,之后不會改變。

var id = 'GLOBAL';
var obj = {
  id: 'OBJ',
  a: function(){
    console.log(this.id);
  },
  b: () => {
    console.log(this.id);
  }
};
obj.a();    // 'OBJ'
obj.b();    // 'GLOBAL'
new obj.a()  // undefined
new obj.b()  // Uncaught TypeError: obj.b is not a constructor

對象obj的方法b是使用箭頭函數(shù)定義的,這個函數(shù)中的this就永遠(yuǎn)指向它定義時所處的全局執(zhí)行環(huán)境中的this,即便這個函數(shù)是作為對象obj的方法調(diào)用,this依舊指向Window對象。需要注意,定義對象的大括號{}是無法形成一個單獨(dú)的執(zhí)行環(huán)境的,它依舊是處于全局執(zhí)行環(huán)境中。

同樣,使用call()、apply()、bind()等方法也不能改變箭頭函數(shù)中this的指向:

var id = 'Global';
let fun1 = () => {
    console.log(this.id)
};
fun1();                     // 'Global'
fun1.call({id: 'Obj'});     // 'Global'
fun1.apply({id: 'Obj'});    // 'Global'
fun1.bind({id: 'Obj'})();   // 'Global'

(3)不可作為構(gòu)造函數(shù)

構(gòu)造函數(shù) new 操作符的執(zhí)行步驟如下:

  1. 創(chuàng)建一個對象
  2. 將構(gòu)造函數(shù)的作用域賦給新對象(也就是將對象的__proto__屬性指向構(gòu)造函數(shù)的prototype屬性)
  3. 指向構(gòu)造函數(shù)中的代碼,構(gòu)造函數(shù)中的this指向該對象(也就是為這個對象添加屬性和方法)
  4. 返回新的對象

實(shí)際上第二步就是將函數(shù)中的this指向該對象。但是由于箭頭函數(shù)時沒有自己的this的,且this指向外層的執(zhí)行環(huán)境,且不能改變指向,所以不能當(dāng)做構(gòu)造函數(shù)使用。

(4)不綁定 arguments

箭頭函數(shù)沒有自己的arguments對象。在箭頭函數(shù)中訪問arguments實(shí)際上獲得的是它外層函數(shù)的arguments值。

6、擴(kuò)展運(yùn)算符

擴(kuò)展運(yùn)算符:...  就像是rest參數(shù)的逆運(yùn)算,將一個數(shù)組轉(zhuǎn)為用逗號分隔的參數(shù)序列,對數(shù)組進(jìn)行解包。

spread 擴(kuò)展運(yùn)算符有以下用途

將數(shù)組轉(zhuǎn)化為用逗號分隔的參數(shù)序列:

function  test(a,b,c){
    console.log(a); // 1
    console.log(b); // 2
    console.log(c); // 3
}

var arr = [1, 2, 3];
test(...arr);

將一個數(shù)組拼接到另一個數(shù)組:

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

將字符串轉(zhuǎn)為逗號分隔的數(shù)組:

var str='JavaScript';
var arr= [...str];
console.log(arr); // ["J", "a", "v", "a", "S", "c", "r", "i", "p", "t"]

7、Symbol

ES6中引入了一個新的基本數(shù)據(jù)類型Symbol,表示獨(dú)一無二的值。它是一種類似于字符串的數(shù)據(jù)類型,它的特點(diǎn)如下:

  • Symbol的值是唯一的,用來解決命名沖突的問題。
  • Symbol值不能與其他類型數(shù)據(jù)進(jìn)行運(yùn)算。
  • Symbol定義的對象屬性不能使用for...in遍歷循環(huán),但是可以使用Reflect.ownKeys 來獲取對象的所有鍵名。
let s1 = Symbol();
console.log(typeof s1); // "symbol"

let s2 = Symbol('hello');
let s3 = Symbol('hello');
console.log(s2 === s3); // false

基于以上特性,Symbol 屬性類型比較適合用于兩類場景中:常量值和對象屬性。

(1)避免常量值重復(fù)

getValue 函數(shù)會根據(jù)傳入字符串參數(shù) key 執(zhí)行對應(yīng)代碼邏輯:

function getValue(key) {
  switch(key){
    case 'A':
      ...
    case 'B':
      ...
  }
}
getValue('B');

這段代碼對調(diào)用者而言非常不友好,因?yàn)榇a中使用了魔術(shù)字符串(Magic string,指的是在代碼之中多次出現(xiàn)、與代碼形成強(qiáng)耦合的某一個具體的字符串或者數(shù)值),導(dǎo)致調(diào)用 getValue 函數(shù)時需要查看函數(shù)代碼才能找到參數(shù) key 的可選值。所以可以將參數(shù) key 的值以常量的方式聲明:

const KEY = {
  alibaba: 'A',
  baidu: 'B',
}
function getValue(key) {
  switch(key){
    case KEY.alibaba:
      ...
    case KEY.baidu:
      ...
  }
}
getValue(KEY.baidu);

但這樣也并非完美,假設(shè)現(xiàn)在要在 KEY 常量中加入一個 key,根據(jù)對應(yīng)的規(guī)則,很有可能會出現(xiàn)值重復(fù)的情況:

const KEY = {
  alibaba: 'A',
  baidu: 'B',
  tencent: 'B'
}

這就會出現(xiàn)問題:

getValue(KEY.baidu) // 等同于 getValue(KEY.tencent)

所以在這種場景下更適合使用 Symbol,不需要關(guān)心值本身,只關(guān)心值的唯一性:

const KEY = {
  alibaba: Symbol(),
  baidu: Symbol(),
  tencent: Symbol()
}

(2)避免對象屬性覆蓋

函數(shù) fn 需要對傳入的對象參數(shù)添加一個臨時屬性 user,但可能該對象參數(shù)中已經(jīng)有這個屬性了,如果直接賦值就會覆蓋之前的值。此時就可以使用 Symbol 來避免這個問題。創(chuàng)建一個 Symbol 數(shù)據(jù)類型的變量,然后將該變量作為對象參數(shù)的屬性進(jìn)行賦值和讀取,這樣就能避免覆蓋的情況:

function fn(o) { // {user: {id: xx, name: yy}}
  const s = Symbol()
  o[s] = 'zzz'
}

8、集合 Set

ES6提供了新的數(shù)據(jù)結(jié)構(gòu)Set(集合)。它類似于數(shù)組,但是成員的值都是唯一的,集合實(shí)現(xiàn)了iterator接口,所以可以使用擴(kuò)展運(yùn)算符和 for…of 進(jìn)行遍歷。

Set的屬性和方法:

屬性和方法

概述

size

返回集合的元素個數(shù)

add

增加一個新的元素,返回當(dāng)前的集合

delete

刪除元素,返回布爾值

has

檢查集合中是否包含某元素,返回布爾值

clear

清空集合,返回undefined

//創(chuàng)建一個空集合
let s = new Set();
//創(chuàng)建一個非空集合
let s1 = new Set([1,2,3,1,2,3]);
//返回集合的元素個數(shù)
console.log(s1.size);       // 3
//添加新元素
console.log(s1.add(4));     // {1,2,3,4}
//刪除元素
console.log(s1.delete(1));  //true
//檢測是否存在某個值
console.log(s1.has(2));     // true
//清空集合
console.log(s1.clear());    //undefined

由于集合中元素的唯一性,所以在實(shí)際應(yīng)用中,可以使用set來實(shí)現(xiàn)數(shù)組去重:

let arr = [1,2,3,2,1]
Array.from(new Set(arr))  // {1, 2, 3}

這里使用了Array.form()方法來將數(shù)組集合轉(zhuǎn)化為數(shù)組。

可以通過set來求兩個數(shù)組的交集和并集:

// 模擬求交集 
let intersection = new Set([...set1].filter(x => set2.has(x)));

// 模擬求差集
let difference = new Set([...set1].filter(x => !set2.has(x)));

用以下方法可以進(jìn)行數(shù)組與集合的相互轉(zhuǎn)化:

// Set集合轉(zhuǎn)化為數(shù)組
const arr = [...mySet]
const arr = Array.from(mySet)

// 數(shù)組轉(zhuǎn)化為Set集合
const mySet = new Set(arr)

9、Map

ES6提供了Map數(shù)據(jù)結(jié)構(gòu),它類似于對象,也是鍵值隊(duì)的集合,但是它的鍵值的范圍不限于字符串,可以是任何類型(包括對象)的值,也就是說, Object 結(jié)構(gòu)提供了“ 字符串—值” 的對應(yīng), Map 結(jié)構(gòu)提供了“ 值—值” 的對應(yīng), 是一種更完善的 Hash 結(jié)構(gòu)實(shí)現(xiàn)。如果需要“ 鍵值對” 的數(shù)據(jù)結(jié)構(gòu), Map 比 Object 更合適。Map也實(shí)現(xiàn)了iterator接口,所以可以使用擴(kuò)展運(yùn)算符和 for…of 進(jìn)行遍歷。

Map的屬性和方法:

屬性和方法

概述

size

返回Map的元素個數(shù)

set

增加一個新的元素,返回當(dāng)前的Map

get

返回鍵名對象的鍵值

has

檢查Map中是否包含某元素,返回布爾值

clear

清空Map,返回undefined

//創(chuàng)建一個空 map
let m = new Map();
//創(chuàng)建一個非空 map
let m2 = new Map([
 ['name', 'hello'],
]);
//獲取映射元素的個數(shù)
console.log(m2.size);          // 1
//添加映射值
console.log(m2.set('age', 6)); // {"name" => "hello", "age" => 6}
//獲取映射值
console.log(m2.get('age'));    // 6
//檢測是否有該映射
console.log(m2.has('age'));    // true
//清除
console.log(m2.clear());       // undefined

需要注意, 只有對同一個對象的引用, Map 結(jié)構(gòu)才將其視為同一個鍵:

let map = new Map(); 
map.set(['a'], 555); 
map.get(['a']) // undefined

上面代碼的set和get方法, 表面是針對同一個鍵, 但實(shí)際上這是兩個值, 內(nèi)存地址是不一樣的, 因此get方法無法讀取該鍵, 所以會返回undefined。

由上可知, Map 的鍵實(shí)際上是跟內(nèi)存地址綁定的, 只要內(nèi)存地址不一樣, 就視為兩個鍵。這就解決了同名屬性碰撞( clash) 的問題,在擴(kuò)展庫時, 如果使用對象作為鍵名, 就不用擔(dān)心自己的屬性與原來的屬性同名。

如果 Map 的鍵是一個簡單類型的值( 數(shù)字、 字符串、 布爾值), 則只要兩個值嚴(yán)格相等, Map 將其視為一個鍵, 包括0和 - 0。另外, 雖然NaN不嚴(yán)格相等于自身, 但 Map 將其視為同一個鍵。

let map = new Map(); 
map.set(NaN, 123); 
map.get(NaN) // 123 
map.set(-0, 123); 
map.get(+0) // 123

10、模塊化

ES6中首次引入模塊化開發(fā)規(guī)范ES Module,讓Javascript首次支持原生模塊化開發(fā)。ES Module把一個文件當(dāng)作一個模塊,每個模塊有自己的獨(dú)立作用域,那如何把每個模塊聯(lián)系起來呢?核心點(diǎn)就是模塊的導(dǎo)入與導(dǎo)出。

(1)export 導(dǎo)出模塊

  • 正常導(dǎo)出:
// 方式一
export var first = 'test';
export function func() {
    return true;
}

// 方式二
var first = 'test';
var second = 'test';
function func() {
    return true;
}
export {first, second, func};
  • as關(guān)鍵字:
var first = 'test';
export {first as second};

as關(guān)鍵字可以重命名暴露出的變量或方法,經(jīng)過重命名后同一變量可以多次暴露出去。

  • export default

export default會導(dǎo)出默認(rèn)輸出,即用戶不需要知道模塊中輸出的名字,在導(dǎo)入的時候?yàn)槠渲付ㄈ我饷帧?/p>

// 導(dǎo)出
export default function () {
  console.log('foo');
}
// 導(dǎo)入
import customName from './export-default';

注意: 導(dǎo)入默認(rèn)模塊時不需要大括號,導(dǎo)出默認(rèn)的變量或方法可以有名字,但是對外無效。export default只能使用一次。

(2)import 導(dǎo)入模塊

  • 正常導(dǎo)入:
import {firstName, lastName, year} from './profile';
復(fù)制代碼

導(dǎo)入模塊位置可以是相對路徑也可以是絕對路徑,.js可以省略,如果不帶路徑只是模塊名,則需要通過配置文件告訴引擎查找的位置。

  • as關(guān)鍵字:
import { lastName as surname } from './profile';

import 命令會被提升到模塊頭部,所以寫的位置不是那么重要,但是不能使用表達(dá)式和變量來進(jìn)行導(dǎo)入。

  • 加載整個模塊(無輸出)
import 'lodash'; //僅僅是加載而已,無法使用
  • 加載整個模塊(有輸出)
import * as circle from './circle';
console.log('圓面積:' + circle.area(4));
console.log('圓周長:' + circle.circumference(14));

注意: import * 會忽略default輸出

(3)導(dǎo)入導(dǎo)出復(fù)合用法

  • 先導(dǎo)入后導(dǎo)出
export { foo, bar } from 'my_module';
// 等同于
import { foo, bar } from 'my_module';
export { foo, boo};
  • 整體先導(dǎo)入再輸出以及default
// 整體輸出
export * from 'my_module';
// 導(dǎo)出default,正如前面所說,export default 其實(shí)導(dǎo)出的是default變量
export { default } from 'foo';
// 具名接口改default
export { es6 as default } from './someModule';

(4)模塊的繼承

export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
  return Math.exp(x);
}

注意: export * 會忽略default。

11、字符串方法

(1)includes()

includes():該方法用于判斷字符串是否包含指定的子字符串。如果找到匹配的字符串則返回 true,否則返回 false。該方法的語法如下:

string.includes(searchvalue, start)

該方法有兩個參數(shù):

  • searchvalue:必需,要查找的字符串;
  • start:可選,設(shè)置從那個位置開始查找,默認(rèn)為 0。
let str = 'Hello world!';

str.includes('o')  // 輸出結(jié)果:true
str.includes('z')  // 輸出結(jié)果:false
str.includes('e', 2)  // 輸出結(jié)果:false

(2)startsWith()

startsWith():該方法用于檢測字符串是否以指定的子字符串開始。如果是以指定的子字符串開頭返回 true,否則 false。其語法和上面的includes()方法一樣。

let str = 'Hello world!';

str.startsWith('Hello') // 輸出結(jié)果:true
str.startsWith('Helle') // 輸出結(jié)果:false
str.startsWith('wo', 6) // 輸出結(jié)果:true

(3)endsWith()

endsWith():該方法用來判斷當(dāng)前字符串是否是以指定的子字符串結(jié)尾。如果傳入的子字符串在搜索字符串的末尾則返回 true,否則將返回 false。其語法如下:

string.endsWith(searchvalue, length)

該方法有兩個參數(shù):

  • searchvalue:必需,要搜索的子字符串。
  • length:設(shè)置字符串的長度,默認(rèn)值為原始字符串長度 string.length。
let str = 'Hello world!';

str.endsWith('!')       // 輸出結(jié)果:true
str.endsWith('llo')     // 輸出結(jié)果:false
str.endsWith('llo', 5)  // 輸出結(jié)果:true

可以看到,當(dāng)?shù)诙€參數(shù)設(shè)置為5時,就會從字符串的前5個字符中進(jìn)行檢索,所以會返回true。

(4)repeat()

repeat() 方法返回一個新字符串,表示將原字符串重復(fù)n次:

'x'.repeat(3)     // 輸出結(jié)果:"xxx"
'hello'.repeat(2) // 輸出結(jié)果:"hellohello"
'na'.repeat(0)    // 輸出結(jié)果:""

如果參數(shù)是小數(shù),會向下取整:

'na'.repeat(2.9) // 輸出結(jié)果:"nana"

如果參數(shù)是負(fù)數(shù)或者Infinity,會報(bào)錯:

'na'.repeat(Infinity)   // RangeError
'na'.repeat(-1)         // RangeError

如果參數(shù)是 0 到-1 之間的小數(shù),則等同于 0,這是因?yàn)闀冗M(jìn)行取整運(yùn)算。0 到-1 之間的小數(shù),取整以后等于-0,repeat視同為 0。

'na'.repeat(-0.9)   // 輸出結(jié)果:""

如果參數(shù)是NaN,就等同于 0:

'na'.repeat(NaN)    // 輸出結(jié)果:""

如果repeat的參數(shù)是字符串,則會先轉(zhuǎn)換成數(shù)字。

'na'.repeat('na')   // 輸出結(jié)果:""
'na'.repeat('3')    // 輸出結(jié)果:"nanana"

12、數(shù)組方法

(1)reduce()

reduce() 方法對數(shù)組中的每個元素執(zhí)行一個reducer函數(shù)(升序執(zhí)行),將其結(jié)果匯總為單個返回值。其使用語法如下:

arr.reduce(callback,[initialValue])

reduce 為數(shù)組中的每一個元素依次執(zhí)行回調(diào)函數(shù),不包括數(shù)組中被刪除或從未被賦值的元素,接受四個參數(shù):初始值(或者上一次回調(diào)函數(shù)的返回值),當(dāng)前元素值,當(dāng)前索引,調(diào)用 reduce 的數(shù)組。

callback (執(zhí)行數(shù)組中每個值的函數(shù),包含四個參數(shù))

  • previousValue (上一次調(diào)用回調(diào)返回的值,或者是提供的初始值(initialValue))
  • currentValue (數(shù)組中當(dāng)前被處理的元素)
  • index (當(dāng)前元素在數(shù)組中的索引)
  • array (調(diào)用 reduce 的數(shù)組)

initialValue (作為第一次調(diào)用 callback 的第一個參數(shù)。)

let arr = [1, 2, 3, 4]
let sum = arr.reduce((prev, cur, index, arr) => {
    console.log(prev, cur, index);
    return prev + cur;
})
console.log(arr, sum);

輸出結(jié)果如下:

1 2 1
3 3 2
6 4 3
[1, 2, 3, 4] 10

再來加一個初始值看看:

let arr = [1, 2, 3, 4]
let sum = arr.reduce((prev, cur, index, arr) => {
    console.log(prev, cur, index);
    return prev + cur;
}, 5)
console.log(arr, sum);

輸出結(jié)果如下:

5 1 0
6 2 1
8 3 2
11 4 3
[1, 2, 3, 4] 15

通過上面例子,可以得出結(jié)論:如果沒有提供initialValue,reduce 會從索引1的地方開始執(zhí)行 callback 方法,跳過第一個索引。如果提供initialValue,從索引0開始。

注意,該方法如果添加初始值,就會改變原數(shù)組,將這個初始值放在數(shù)組的最后一位。

(2)filter()

filter()方法用于過濾數(shù)組,滿足條件的元素會被返回。它的參數(shù)是一個回調(diào)函數(shù),所有數(shù)組元素依次執(zhí)行該函數(shù),返回結(jié)果為true的元素會被返回。該方法會返回一個新的數(shù)組,不會改變原數(shù)組。

let arr = [1, 2, 3, 4, 5]
arr.filter(item => item > 2) 
// 結(jié)果:[3, 4, 5]

可以使用filter()方法來移除數(shù)組中的undefined、null、NAN等值。

let arr = [1, undefined, 2, null, 3, false, '', 4, 0]
arr.filter(Boolean)
// 結(jié)果:[1, 2, 3, 4]

(3)Array.from

Array.from 的設(shè)計(jì)初衷是快速基于其他對象創(chuàng)建新數(shù)組,準(zhǔn)確來說就是從一個類似數(shù)組的可迭代對象中創(chuàng)建一個新的數(shù)組實(shí)例。其實(shí),只要一個對象有迭代器,Array.from 就能把它變成一個數(shù)組(注意:該方法會返回一個的數(shù)組,不會改變原對象)。

從語法上看,Array.from 有 3 個參數(shù):

  • 類似數(shù)組的對象,必選。
  • 加工函數(shù),新生成的數(shù)組會經(jīng)過該函數(shù)的加工再返回。
  • this 作用域,表示加工函數(shù)執(zhí)行時 this 的值。

這三個參數(shù)里面第一個參數(shù)是必選的,后兩個參數(shù)都是可選的:

var obj = {0: 'a', 1: 'b', 2:'c', length: 3};

Array.from(obj, function(value, index){
  console.log(value, index, this, arguments.length);
  return value.repeat(3);   //必須指定返回值,否則返回 undefined
}, obj);

結(jié)果如圖:

以上結(jié)果表明,通過 Array.from 這個方法可以自定義加工函數(shù)的處理方式,從而返回想要得到的值;如果不確定返回值,則會返回 undefined,最終生成的是一個包含若干個 undefined 元素的空數(shù)組。

實(shí)際上,如果這里不指定 this,加工函數(shù)就可以是一個箭頭函數(shù)。上述代碼可以簡寫為以下形式。

Array.from(obj, (value) => value.repeat(3));
//  控制臺打印 (3) ["aaa", "bbb", "ccc"]

除了上述 obj 對象以外,擁有迭代器的對象還包括 String、Set、Map 等,Array.from 都可以進(jìn)行處理:

// String
Array.from('abc');                             // ["a", "b", "c"]
// Set
Array.from(new Set(['abc', 'def']));           // ["abc", "def"]
// Map
Array.from(new Map([[1, 'ab'], [2, 'de']]));   // [[1, 'ab'], [2, 'de']]

(4)fill()

使用fill()方法可以向一個已有數(shù)組中插入全部或部分相同的值,開始索引用于指定開始填充的位置,它是可選的。如果不提供結(jié)束索引,則一直填充到數(shù)組末尾。如果是負(fù)值,則將從負(fù)值加上數(shù)組的長度而得到的值開始。該方法的語法如下:

array.fill(value, start, end)

其參數(shù)如下:

  • value:必需。填充的值。
  • start:可選。開始填充位置。
  • end:可選。停止填充位置 (默認(rèn)為 array.length)。

使用示例如下:

const arr = [0, 0, 0, 0, 0];

// 用5填充整個數(shù)組
arr.fill(5);
console.log(arr); // [5, 5, 5, 5, 5]
arr.fill(0);      // 重置

// 用5填充索引大于等于3的元素
arr.fill(5, 3);
console.log(arr); // [0, 0, 0, 5, 5]
arr.fill(0);      // 重置

// 用5填充索引大于等于1且小于等于3的元素
arr.fill(5, 3);
console.log(arr); // [0, 5, 5, 0, 0]
arr.fill(0);      // 重置

// 用5填充索引大于等于-1的元素
arr.fill(5, -1);
console.log(arr); // [0, 0, 0, 0, 5]
arr.fill(0);      // 重置

二、ES7 新特性(2016)

1、Array.includes()

includes() 方法用來判斷一個數(shù)組是否包含一個指定的值,如果包含則返回 true,否則返回false。該方法不會改變原數(shù)組。其語法如下:

arr.includes(searchElement, fromIndex)

該方法有兩個參數(shù):

  • searchElement:必須,需要查找的元素值。
  • fromIndex:可選,從fromIndex 索引處開始查找目標(biāo)值。如果為負(fù)值,則按升序從 array.length + fromIndex 的索引開始搜 (即使從末尾開始往前跳 fromIndex 的絕對值個索引,然后往后搜尋)。默認(rèn)為 0。
[1, 2, 3].includes(2);  //  true
[1, 2, 3].includes(4);  //  false
[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true

在 ES7 之前,通常使用 indexOf 來判斷數(shù)組中是否包含某個指定值。但 indexOf 在語義上不夠明確直觀,同時 indexOf 內(nèi)部使用 === 來判等,所以存在對 NaN 的誤判,includes 則修復(fù)了這個問題:

[1, 2, NaN].indexOf(NaN);   // -1
[1, 2, NaN].includes(NaN);  //  true

注意:使用includes()比較字符串和字符時區(qū)分大小寫。

2、指數(shù)操作符

ES7 還引入了指數(shù)操作符 ,用來更為方便的進(jìn)行指數(shù)計(jì)算,它與 Math.pow() 等效:

Math.pow(2, 10));  // 1024
2**10;           // 1024

三、ES8 新特性(2017)

1、padStart()和padEnd()

padStart()和padEnd()方法用于補(bǔ)齊字符串的長度。如果某個字符串不夠指定長度,會在頭部或尾部補(bǔ)全。

(1)padStart()

padStart()用于頭部補(bǔ)全。該方法有兩個參數(shù),其中第一個參數(shù)是一個數(shù)字,表示字符串補(bǔ)齊之后的長度;第二個參數(shù)是用來補(bǔ)全的字符串。

如果原字符串的長度,等于或大于指定的最小長度,則返回原字符串:

'x'.padStart(1, 'ab') // 'x'

如果用來補(bǔ)全的字符串與原字符串,兩者的長度之和超過了指定的最小長度,則會截去超出位數(shù)的補(bǔ)全字符串:

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

如果省略第二個參數(shù),默認(rèn)使用空格補(bǔ)全長度:

'x'.padStart(4, 'ab') // 'a   '

padStart()的常見用途是為數(shù)值補(bǔ)全指定位數(shù),筆者最近做的一個需求就是將返回的頁數(shù)補(bǔ)齊為三位,比如第1頁就顯示為001,就可以使用該方法來操作:

"1".padStart(3, '0')   // 輸出結(jié)果: '001'
"15".padStart(3, '0')  // 輸出結(jié)果: '015'

(2)padEnd()

padEnd()用于尾部補(bǔ)全。該方法也是接收兩個參數(shù),第一個參數(shù)是字符串補(bǔ)全生效的最大長度,第二個參數(shù)是用來補(bǔ)全的字符串:

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'

2、Object.values()和Object.entries()

在ES5中就引入了Object.keys方法,在ES8中引入了跟Object.keys配套的Object.values和Object.entries,作為遍歷一個對象的補(bǔ)充手段,供for...of循環(huán)使用。它們都用來遍歷對象,它會返回一個由給定對象的自身可枚舉屬性(不含繼承的和Symbol屬性)組成的數(shù)組,數(shù)組元素的排列順序和正常循環(huán)遍歷該對象時返回的順序一致,這個三個元素返回的值分別如下:

  • Object.keys():返回包含對象鍵名的數(shù)組。
  • Object.values():返回包含對象鍵值的數(shù)組。
  • Object.entries():返回包含對象鍵名和鍵值的數(shù)組。
let obj = { 
  id: 1, 
  name: 'hello', 
  age: 18 
};
console.log(Object.keys(obj));   // 輸出結(jié)果: ['id', 'name', 'age']
console.log(Object.values(obj)); // 輸出結(jié)果: [1, 'hello', 18]
console.log(Object.entries(obj));   // 輸出結(jié)果: [['id', 1], ['name', 'hello'], ['age', 18]

注意

  • Object.keys()方法返回的數(shù)組中的值都是字符串,也就是說不是字符串的key值會轉(zhuǎn)化為字符串。
  • 結(jié)果數(shù)組中的屬性值都是對象本身可枚舉的屬性,不包括繼承來的屬性。

3、函數(shù)擴(kuò)展

ES2017 規(guī)定函數(shù)的參數(shù)列表的結(jié)尾可以為逗號:

function person( name, age, sex, ) {}

該特性的主要作用是方便使用git進(jìn)行多人協(xié)作開發(fā)時修改同一個函數(shù)減少不必要的行變更。

4、Object.values

之前可以通過 Object.keys 來獲取一個對象所有的 key。在ES8中提供了 Object.values 來獲取對象所有的 value 值:

const person = {
  name: "zhangsan",
  age: 18,
  height: 188,
};

console.log(Object.values(person)); // ['zhangsan', 18, 188]

四、ES9 新特性(2018)

1、for await…of

for await...of方法被稱為異步迭代器,該方法是主要用來遍歷異步對象。

for await...of 語句會在異步或者同步可迭代對象上創(chuàng)建一個迭代循環(huán),包括 String,Array,類數(shù)組,Map, Set和自定義的異步或者同步可迭代對象。這個語句只能在 async function內(nèi)使用:

function Gen (time) {
  return new Promise((resolve,reject) => {
    setTimeout(function () {
       resolve(time)
    },time)
  })
}

async function test () {
   let arr = [Gen(2000),Gen(100),Gen(3000)]
   for await (let item of arr) {
      console.log(Date.now(),item)
   }
}
test()

輸出結(jié)果:

2、Promise.finally

ES2018 為 Promise 添加了 finally() 方法,表示無論 Promise 實(shí)例最終成功或失敗都會執(zhí)行的方法:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const one = '1';
    reject(one);
  }, 1000);
});

promise
  .then(() => console.log('success'))
  .catch(() => console.log('fail'))
  .finally(() => console.log('finally'))

finally() 函數(shù)不接受參數(shù),finally() 內(nèi)部通常不知道 promise 實(shí)例的執(zhí)行結(jié)果,所以通常在 finally() 方法內(nèi)執(zhí)行的是與 promise 狀態(tài)無關(guān)的操作。

3、對象的擴(kuò)展運(yùn)算符

在ES6中就引入了擴(kuò)展運(yùn)算符,但是它只能作用于數(shù)組,ES2018中的擴(kuò)展運(yùn)算符可以作用于對象:

將元素組織成對象。

const obj = {a: 1, b: 2, c: 3};
const {a, ...rest} = obj;
console.log(rest);    // 輸出 {b: 2, c: 3}

(function({a, ...obj}) {
  console.log(obj);    // 輸出 {b: 2, c: 3}
}({a: 1, b: 2, c: 3}));

將對象擴(kuò)展為元素。

const obj = {a: 1, b: 2, c: 3};
const newObj ={...obj, d: 4};
console.log(newObj);  // 輸出 {a: 1, b: 2, c: 3, d: 4}

可以用來合并對象。

const obj1 = {a: 1, b:2};
const obj2 = {c: 3, d:4};
const mergedObj = {...obj1, ...obj2};
console.log(mergedObj);  // 輸出 {a: 1, b: 2, c: 3, d: 4}

4、對象的 Rest

在對象的解構(gòu)中,除了已經(jīng)指定的屬性之外,rest將會拷貝對象其他的所有可枚舉屬性:

const obj = {foo: 1, bar: 2, baz: 3};
const {foo, ...rest} = obj;

console.log(rest); // {bar: 2, baz: 3}

如果用在函數(shù)參數(shù)中,rest 表示所有剩下的參數(shù):

function func({param1, ...rest}) {
    return rest;
}

console.log(func({param1:1, b:2, c:3, d:4}))  // {b: 2, c: 3, d: 4}

注意,在對象字面量中,rest運(yùn)算符只能放在對象的最頂層,并且只能使用一次,要放在最后:

const {...rest, foo} = obj; // Uncaught SyntaxError: Rest element must be last element
const {foo, ...rest1, ...rest2} = obj; // Uncaught SyntaxError: Rest element must be last element

五、ES10 新特性(2019)

1、trimStart() 和 trimEnd()

在ES10之前,JavaScript提供了trim()方法,用于移除字符串首尾空白符。在ES9中提出了trimStart()和trimEnd() 方法用于移除字符串首尾的頭尾空白符,空白符包括:空格、制表符 tab、換行符等其他空白符等。

(1)trimStart()

trimStart() 方法的的行為與trim()一致,不過會返回一個從原始字符串的開頭刪除了空白的新字符串,不會修改原始字符串:

const s = '  abc  ';

s.trimStart()   // "abc  "

(2)trimStart()

trimEnd() 方法的的行為與trim()一致,不過會返回一個從原始字符串的結(jié)尾刪除了空白的新字符串,不會修改原始字符串:

const s = '  abc  ';

s.trimEnd()   // "  abc"

注意,這兩個方法都不適用于null、undefined、Number類型。

2、flat()和flatMap()

(1)flat()

在ES2019中,flat()方法用于創(chuàng)建并返回一個新數(shù)組,這個新數(shù)組包含與它調(diào)用flat()的數(shù)組相同的元素,只不過其中任何本身也是數(shù)組的元素會被打平填充到返回的數(shù)組中:

[1, [2, 3]].flat()        // [1, 2, 3]
[1, [2, [3, 4]]].flat()   // [1, 2, [3, 4]]

在不傳參數(shù)時,flat()默認(rèn)只會打平一級嵌套,如果想要打平更多的層級,就需要傳給flat()一個數(shù)值參數(shù),這個參數(shù)表示要打平的層級數(shù):

[1, [2, [3, 4]]].flat(2)  // [1, 2, 3, 4]

如果數(shù)組中存在空項(xiàng),會直接跳過:

[1, [2, , 3]].flat());    //  [1, 2, 3]

如果傳入的參數(shù)小于等于0,就會返回原數(shù)組:

[1, [2, [3, [4, 5]]]].flat(0);    //  [1, [2, [3, [4, 5]]]]
[1, [2, [3, [4, 5]]]].flat(-10);  //  [1, [2, [3, [4, 5]]]]

(2)flatMap()

flatMap()方法使用映射函數(shù)映射每個元素,然后將結(jié)果壓縮成一個新數(shù)組。它與 map 和連著深度值為1的 flat 幾乎相同,但 flatMap 通常在合并成一種方法的效率稍微高一些。該方法會返回一個新的數(shù)組,其中每個元素都是回調(diào)函數(shù)的結(jié)果,并且結(jié)構(gòu)深度 depth 值為1。

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

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

3. Object.fromEntries()

Object.fromEntries()方法可以把鍵值對列表轉(zhuǎn)換為一個對象。該方法相當(dāng)于 Object.entries() 方法的逆過程。Object.entries()方法返回一個給定對象自身可枚舉屬性的鍵值對數(shù)組,而Object.fromEntries() 方法把鍵值對列表轉(zhuǎn)換為一個對象。

const object = { key1: 'value1', key2: 'value2' }
 
const array = Object.entries(object)  // [ ["key1", "value1"], ["key2", "value2"] ]
 
 
Object.fromEntries(array)             // { key1: 'value1', key2: 'value2' }

使用該方法主要有以下兩個用途:

(1)將數(shù)組轉(zhuǎn)成對象

const entries = [
  ['foo', 'bar'],
  ['baz', 42]
]
Object.fromEntries(entries)  //  { foo: "bar", baz: 42 }

(2)將 Map 轉(zhuǎn)成對象

const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
])
Object.fromEntries(entries)  //  { foo: "bar", baz: 42 }

4、Symbol描述

通過 Symbol() 創(chuàng)建符號時,可以通過參數(shù)提供字符串作為描述:

let dog = Symbol("dog");  // dog 為描述

在 ES2019 之前,獲取一個 Symbol 值的描述需要通過 String 方法 或 toString 方法:

String(dog);              // "Symbol(dog)" 
dog.toString();           // "Symbol(dog)"

ES2019 補(bǔ)充了屬性 description,用來直接訪問描述

dog.description;  // dog

5、toString()

ES2019 對函數(shù)的 toString() 方法進(jìn)行了擴(kuò)展,以前這個方法只會輸出函數(shù)代碼,但會省略注釋和空格。ES2019 的 toString()則會保留注釋、空格等,即輸出的是原始代碼:

function sayHi() {
  /* dog */
  console.log('wangwang');
}

sayHi.toString();  // 將輸出和上面一樣的原始代碼

6、catch

在 ES2019 以前,catch 會帶有參數(shù),但是很多時候 catch 塊是多余的。而現(xiàn)在可以不帶參數(shù):

// ES2019 之前
try {
   ...
} catch(error) {
   ...
}

// ES2019 之后
try {
   ...
} catch {
   ...
}

六、ES11 新特性(2020)

1、BigInt

在 JavaScript 中,數(shù)值類型 Number 是 64 位浮點(diǎn)數(shù),所以計(jì)算精度和表示范圍都有一定限制。ES2020 新增了 BigInt 數(shù)據(jù)類型,這也是 JavaScript 引入的第八種基本類型。BigInt 可以表示任意大的整數(shù)。其語法如下:

BigInt(value);

其中 value 是創(chuàng)建對象的數(shù)值。可以是字符串或者整數(shù)。

在 JavaScript 中,Number 基本類型可以精確表示的最大整數(shù)是253。因此早期會有這樣的問題:

let max = Number.MAX_SAFE_INTEGER;    // 最大安全整數(shù)

let max1 = max + 1
let max2 = max + 2

max1 === max2   // true

有了BigInt之后,這個問題就不復(fù)存在了:

let max = BigInt(Number.MAX_SAFE_INTEGER);

let max1 = max + 1n
let max2 = max + 2n

max1 === max2   // false

可以通過typeof操作符來判斷變量是否為BigInt類型(返回字符串"bigint"):

typeof 1n === 'bigint'; // true 
typeof BigInt('1') === 'bigint'; // true

還可以通過Object.prototype.toString方法來判斷變量是否為BigInt類型(返回字符串"[object BigInt]"):

Object.prototype.toString.call(10n) === '[object BigInt]';    // true

注意,BigInt 和 Number 不是嚴(yán)格相等的,但是寬松相等:

10n === 10 // false 
10n == 10  // true

Number 和 BigInt 可以進(jìn)行比較:

1n < 2;    // true 
2n > 1;    // true 
2 > 2;     // false 
2n > 2;    // false 
2n >= 2;   // true

2、空值合并運(yùn)算符(??)

在編寫代碼時,如果某個屬性不為 null 和 undefined,那么就獲取該屬性,如果該屬性為 null 或 undefined,則取一個默認(rèn)值:

const name = dogName ? dogName : 'default';

可以通過 || 來簡化:

const name =  dogName || 'default';

但是 || 的寫法存在一定的缺陷,當(dāng) dogName 為 0 或 false 的時候也會走到 default 的邏輯。所以 ES2020 引入了 ?? 運(yùn)算符。只有 ?? 左邊為 null 或 undefined時才返回右邊的值:

const dogName = false; 
const name =  dogName ?? 'default';  // name = false;

3、可選鏈操作符(?.)

在開發(fā)過程中,我們經(jīng)常需要獲取深層次屬性,例如 system.user.addr.province.name。但在獲取 name 這個屬性前需要一步步的判斷前面的屬性是否存在,否則并會報(bào)錯:

const name = (system && system.user && system.user.addr && system.user.addr.province && system.user.addr.province.name) || 'default';

為了簡化上述過程,ES2020 引入了「鏈判斷運(yùn)算符」?.,可選鏈操作符( ?. )允許讀取位于連接對象鏈深處的屬性的值,而不必明確驗(yàn)證鏈中的每個引用是否有效。?. 操作符的功能類似于 . 鏈?zhǔn)讲僮鞣?,不同之處在于,在引用為null 或 undefined 的情況下不會引起錯誤,該表達(dá)式短路返回值是 undefined。與函數(shù)調(diào)用一起使用時,如果給定的函數(shù)不存在,則返回 undefined。

const name = system?.user?.addr?.province?.name || 'default';

當(dāng)嘗試訪問可能不存在的對象屬性時,可選鏈操作符將會使表達(dá)式更短、更簡明。在探索一個對象的內(nèi)容時,如果不能確定哪些屬性必定存在,可選鏈操作符也是很有幫助的。

可選鏈有以下三種形式:

a?.[x]
// 等同于
a == null ? undefined : a[x]

a?.b()
// 等同于
a == null ? undefined : a.b()

a?.()
// 等同于
a == null ? undefined : a()

在使用TypeScript開發(fā)時,這個操作符可以解決很多問題。

4、Promise.allSettled

Promise.allSettled 的參數(shù)接受一個 Promise 的數(shù)組,返回一個新的 Promise。唯一的不同在于,執(zhí)行完之后不會失敗,也就是說當(dāng) Promise.allSettled 全部處理完成后,我們可以拿到每個 Promise 的狀態(tài),而不管其是否處理成功。

下面使用 allSettled 實(shí)現(xiàn)的一段代碼:

const resolved = Promise.resolve(2);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
  console.log(results);
});
// 返回結(jié)果:
// [
//    { status: 'fulfilled', value: 2 },
//    { status: 'rejected', reason: -1 }
// ]

可以看到,Promise.allSettled 最后返回的是一個數(shù)組,記錄傳進(jìn)來的參數(shù)中每個 Promise 的返回值,這就是和 all 方法不太一樣的地方。你也可以根據(jù) all 方法提供的業(yè)務(wù)場景的代碼進(jìn)行改造,其實(shí)也能知道多個請求發(fā)出去之后,Promise 最后返回的是每個參數(shù)的最終狀態(tài)。

5、String.matchAll()

matchAll() 是新增的字符串方法,它返回一個包含所有匹配正則表達(dá)式的結(jié)果及分組捕獲組的迭代器。因?yàn)榉祷氐氖潜闅v器,所以通常使用for...of循環(huán)取出。

for (const match of 'abcabc'.matchAll(/a/g)) {
    console.log(match)
}
//["a", index: 0, input: "abcabc", groups: undefined]
//["a", index: 3, input: "abcabc", groups: undefined]

需要注意,該方法的第一個參數(shù)是一個正則表達(dá)式對象,如果傳的參數(shù)不是一個正則表達(dá)式對象,則會隱式地使用 new RegExp(obj) 將其轉(zhuǎn)換為一個 RegExp 。另外,RegExp必須是設(shè)置了全局模式g的形式,否則會拋出異常 TypeError。

七、ES12 新特性(2021)

1、String.replaceAll()

replaceAll()方法會返回一個全新的字符串,所有符合匹配規(guī)則的字符都將被替換掉,替換規(guī)則可以是字符串或者正則表達(dá)式。

let string = 'hello world, hello ES12'
string.replace(/hello/g,'hi')    // hi world, hi ES12
string.replaceAll('hello','hi')  // hi world, hi ES12

注意的是,replaceAll 在使用正則表達(dá)式的時候,如果非全局匹配(/g),會拋出異常:

let string = 'hello world, hello ES12'
string.replaceAll(/hello/,'hi') 
// Uncaught TypeError: String.prototype.replaceAll called with a non-global

2、數(shù)字分隔符

數(shù)字分隔符可以在數(shù)字之間創(chuàng)建可視化分隔符,通過 _下劃線來分割數(shù)字,使數(shù)字更具可讀性,可以放在數(shù)字內(nèi)的任何地方:

const money = 1_000_000_000
//等價于
const money = 1000000000

該新特性同樣支持在八進(jìn)制數(shù)中使用:

const number = 0o123_456
//等價于
const number = 0o123456

3、Promise.any

Promise.any是是 ES2021 新增的特性,它接收一個 Promise 可迭代對象(例如數(shù)組),只要其中的一個 promise 成功,就返回那個已經(jīng)成功的 promise 如果可迭代對象中沒有一個 promise 成功(即所有的 promises 都失敗/拒絕),就返回一個失敗的 promise 和 AggregateError 類型的實(shí)例,它是 Error 的一個子類,用于把單一的錯誤集合在一起

const promises = [
  Promise.reject('ERROR A'),
  Promise.reject('ERROR B'),
  Promise.resolve('result'),
]

Promise.any(promises).then((value) => {
  console.log('value: ', value)
}).catch((err) => {
  console.log('err: ', err)
})

// 輸出結(jié)果:value:  result

如果所有傳入的 promises 都失?。?/p>

const promises = [
  Promise.reject('ERROR A'),
  Promise.reject('ERROR B'),
  Promise.reject('ERROR C'),
]

Promise.any(promises).then((value) => {
  console.log('value:', value)
}).catch((err) => {
  console.log('err:', err)
  console.log(err.message)
  console.log(err.name)
  console.log(err.errors)
})

輸出結(jié)果:

err:AggregateError: All promises were rejected
All promises were rejected
AggregateError
["ERROR A", "ERROR B", "ERROR C"]

4、邏輯賦值操作符

ES12中新增了幾個邏輯賦值操作符,可以用來簡化一些表達(dá)式:

// 等同于 a = a || b
a ||= b;
// 等同于 c = c && d
c &&= d;
// 等同于 e = e ?? f
e ??= f;

八、ES13 新特性(2022)

1、Object.hasOwn()

在ES2022之前,可以使用 Object.prototype.hasOwnProperty() 來檢查一個屬性是否屬于對象。

Object.hasOwn 特性是一種更簡潔、更可靠的檢查屬性是否直接設(shè)置在對象上的方法:

const example = {
  property: '123'
};

console.log(Object.prototype.hasOwnProperty.call(example, 'property'));
console.log(Object.hasOwn(example, 'property'));

2、at()

at() 是一個數(shù)組方法,用于通過給定索引來獲取數(shù)組元素。當(dāng)給定索引為正時,這種新方法與使用括號表示法訪問具有相同的行為。當(dāng)給出負(fù)整數(shù)索引時,就會從數(shù)組的最后一項(xiàng)開始檢索:

const array = [0,1,2,3,4,5];

console.log(array[array.length-1]);  // 5
console.log(array.at(-1));  // 5

console.log(array[array.lenght-2]);  // 4
console.log(array.at(-2));  // 4

除了數(shù)組,字符串也可以使用at()方法進(jìn)行索引:

const str = "hello world";

console.log(str[str.length - 1]);  // d
console.log(str.at(-1));  // d

3、error.cause

在 ECMAScript 2022 規(guī)范中,new Error() 中可以指定導(dǎo)致它的原因:

function readFiles(filePaths) {
  return filePaths.map(
    (filePath) => {
      try {
        // ···
      } catch (error) {
        throw new Error(
          `While processing ${filePath}`,
          {cause: error}
        );
      }
    });
}

4、Top-level Await

在ES2017中,引入了 async 函數(shù)和 await 關(guān)鍵字,以簡化 Promise 的使用,但是 await 關(guān)鍵字只能在 async 函數(shù)內(nèi)部使用。嘗試在異步函數(shù)之外使用 await 就會報(bào)錯:SyntaxError - SyntaxError: await is only valid in async function。

頂層 await 允許我們在 async 函數(shù)外面使用 await 關(guān)鍵字。它允許模塊充當(dāng)大型異步函數(shù),通過頂層 await,這些 ECMAScript 模塊可以等待資源加載。這樣其他導(dǎo)入這些模塊的模塊在執(zhí)行代碼之前要等待資源加載完再去執(zhí)行。

由于 await 僅在 async 函數(shù)中可用,因此模塊可以通過將代碼包裝在 async 函數(shù)中來在代碼中包含 await:

// a.js
  import fetch  from "node-fetch";
  let users;

  export const fetchUsers = async () => {
    const resp = await fetch('https://jsonplaceholder.typicode.com/users');
    users =                                                 
網(wǎng)站欄目:ES6—ES14新特性一覽
URL網(wǎng)址:http://www.dlmjj.cn/article/djddpgj.html