新聞中心
一、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í)行步驟如下:
- 創(chuàng)建一個對象
- 將構(gòu)造函數(shù)的作用域賦給新對象(也就是將對象的__proto__屬性指向構(gòu)造函數(shù)的prototype屬性)
- 指向構(gòu)造函數(shù)中的代碼,構(gòu)造函數(shù)中的this指向該對象(也就是為這個對象添加屬性和方法)
- 返回新的對象
實(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 // trueNumber 和 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


咨詢
建站咨詢
