目錄
5、Object.getOwnPropertyDescriptors()
10、Promise.prototype.finally()
14、String.prototype.trimStart()
18、String.prototype.matchAll()
20、String.prototype.replaceAll()
學習並運用一些合適的技術,可以少走彎路,提高我們的開發效率。以下是 ES7-ES12 部分知識彙總,熟練並掌握,在必要時,總能幫助我們解決一些問題以及提升自己的開發效率。
includes()
方法用來判斷一個陣列中是否包含某個指定的值,包含則返回 true
,否則返回 false
。
語法
arr.includes(findVal[, index])
index
可選,表示從 index
索引處開始往後查詢。如果為負值,表示從末尾開始往前跳 |index| 個索引,然後往後查詢。預設值為0。
let arr = ['leo', 'lion', 'ggj']
console.log(arr.includes('leo')) // true
console.log(arr.includes('gao')) // false
console.log(arr.includes('ggj',1)) // true
console.log(arr.includes('Leo')) // false
// 注意:使用includes()查詢字串是區分大小寫的。
求某個數的冪,之前一般使用 Math.pow()
console.log(Math.pow(2, 10)); // 1024
使用冪運運算元 **
console.log(2 ** 10); // 1024
// 注意:冪運運算元的兩個*號之間不能出現空格,否則語法會報錯。
Object.values()
返回一個新陣列,裡面的成員是傳入的引數物件自身的(不含繼承的)所有可遍歷屬性對應的值
let obj = {
name: "leo",
age: 18,
like: "music"
};
console.log(Object.values(obj)); // [ 'leo', 18, 'music' ]
Object.entries
()
方法返回一個新陣列,裡面的成員是傳入的引數物件自身的(不含繼承的)所有可遍歷屬性的鍵值對陣列
let obj = {
name: "leo",
age: 18,
like: "music"
};
console.log(Object.entries(obj)); // [ [ 'name', 'leo' ], [ 'age', 18 ], [ 'like', 'music' ]
擴充套件:Object.keys()
方法返回一個新陣列,裡面的成員是傳入的引數物件自身的(不含繼承的)所有可遍歷的屬性
let obj = {
name: "leo",
age: 18,
like: "music"
};
console.log(Object.keys(obj)); // [ 'name', 'age', 'like' ]
// 注意:如果傳入的引數是陣列,則返回的是所有可遍歷的索引。
Object.getOwnPropertyDescriptors()
方法用來獲取傳入物件的所有自身屬性的描述符
let obj = {
name: "leo",
age: 18,
like: "music"
};
let desc = Object.getOwnPropertyDescriptors(obj);
console.log(desc);
// 列印結果
{
name: {
value: 'leo', // value表示當前物件的預設值
writable: true, // writable表示物件屬性是否可以修改
enumerable: true, // enumerable表示當前這個屬性是否可以出現在物件的列舉屬性中
configurable: true // 表示當前物件的屬效能否用delete刪除
},
age: {
value: 18,
writable: true,
enumerable: true,
configurable: true
},
like: {
value: 'music',
writable: true,
enumerable: true,
configurable: true
}
}
我們可以使用Object.defineProperty()
修改屬性
let obj = {};
Object.defineProperty(obj, "name", {
value: "leo",
writable: false, // 設定為false,該屬性無法修改
configurable: false, // 設定為false,該屬性不支援delete刪除
enumerable: false, // 設定為false,該屬性不可被列舉
});
驗證
console.log(obj); // { name: 'leo' }
obj.name = "lion";
console.log(obj); // { name: 'leo' }
delete obj.name
console.log(obj); // { name: 'leo' }
for (let key in obj) {
console.log(key); // ""
}
把指定字串填充到字串頭部,返回新字串。
語法
str.padStart(targetLength [, padString])
targetLength:
當前字串需要填充到的目標長度。如果這個數值小於當前字串的長度,則返回當前字串本身。
padString:
可選,如果字串太長,使填充後的字串長度超過了目標長度,則只保留最左側的部分,其他部分會被截斷。預設值為 " "。
'abc'.padStart(10); // " abc",填充預設值""
'abc'.padStart(10, "*"); // "*******abc"
'abc'.padStart(6,"123465"); // "123abc"
'abc'.padStart(1); // "abc"
舉例
日期格式化:yyyy-mm-dd的格式
let date = new Date();
let year = date.getFullYear();
// 月日如果是一位前面給它填充一個0
let month = (date.getMonth() + 1).toString().padStart(2, '0');
let day = (date.getDate()).toString().padStart(2, '0');
console.log( `${year}-${month}-${day}` ); // 2022-01-09
數位替換(手機號,銀行卡號等)
let tel = '18937640746'
let newTel = tel.slice(-4).padStart(tel.length, '*')
console.log(newTel) // *******0746
把指定字串填充到字串尾部,返回新字串。
語法與padStart相同
'abc'.padEnd(10); // "abc ",填充預設值""
'abc'.padEnd(10, "*"); // "abc*******"
'abc'.padEnd(6, "123456"); // "abc123"
'abc'.padEnd(1); // "abc"
前面新增了async的函數在執行後都會自動返回一個Promise物件
function fn() {
return 'leo';
}
console.log(fn()); // 'leo'
新增async後
async function fn() {
return 'leo'; // Promise.resolve('leo')
}
console.log(fn()); // Promise物件
async函數中使用await,那麼await處的程式碼就會變成同步的,即只有等await後面的Promise執行完成得到結果才會繼續往下執行,await意為等待
function timeout() {
return new Promise(resolve => {
setTimeout(() => {
console.log(1);
resolve();
}, 1000);
})
}
async function fn() {
await timeout(); // 執行完成才繼續往下執行
console.log(2);
}
fn(); // 不加async和await是 2、1,加了是 1、2
注意:
1、await 只能在 async 標記的函數內部使用,單獨使用會觸發 Syntax error;
2、await後面需要跟非同步操作,不然就沒有意義,而且await後面的Promise物件不必寫then,因為await的作用之一就是獲取後面Promise物件成功狀態傳遞出來的引數。
更多詳情請參考(具體場景)
spread 例子
let text = {
a: 1,
b: 2,
c: 3,
}
let newText = {
...text,
c: 4
}
console.log(newText); // {a: 1, b: 2, c: 4}
這塊程式碼展示了 spread 語法,可以把 text 物件的資料都拓展到 newText 物件,這個功能很實用。需要注意的是,如果存在相同的屬性名,後面的會把前面的覆蓋。
如果屬性的值是一個物件的話,該物件的參照會被拷貝,而不是生成一個新的物件
let obj = { x: { y: 10 } };
let copy1 = { ...obj };
let copy2 = { ...obj };
obj.x.y = "leo";
console.log(copy1, copy2); // {x: {y: "leo"}} {x: {y: "leo"}}
console.log(copy1.x === copy2.x); // true,x為同一個參照
rest 例子
let text = {
a: 1,
b: 2,
c: 3
}
let { a, ...rest } = text
console.log(a, rest); // 1 {b: 2, c: 3}
當物件 key-value 不確定的時候,把必選的 key 賦值給變數,用一個變數接收其他可選的 key 資料,這在之前是做不到的。注意,rest 屬性必須始終出現在物件的末尾,否則將丟擲錯誤。
非同步迭代器(for-await-of):迴圈等待每個Promise物件變為resolved狀態才進入下一步。
for...of 是同步執行
function TimeOut(time){
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(time);
}, time);
})
}
async function test() {
let arr = [TimeOut(2000), TimeOut(1000), TimeOut(3000)]; // 非同步迭代器
for (let item of arr) {
console.log(item.then(console.log));
}
}
test();
// 1000
// 2000
// 3000
for of 方法不能遍歷非同步迭代器,得到的結果並不是我們所期待的,此時可以使用for await of
function TimeOut(time) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(time);
}, time);
})
}
async function test() {
let arr = [TimeOut(2000), TimeOut(1000), TimeOut(3000)];
for await (let item of arr) { // 按arr中順序執行且返回
console.log(item);
}
}
test();
// 2000
// 1000
// 3000
使用for await of,則等待每個Promise物件變為resolved狀態才進入下一步,因此列印的結果為 2000,1000,3000。
Promise執行時,無論結果是fulfilled或者是rejected,在執行then()或catch()後,都會執行finally()指定的回撥函數,可以避免同樣的語句需要在then()和catch()中各寫一次的情況
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
}, 1000);
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
}).finally(() => { // 無論Promise結果是then(),即resolve;還是catch(),即reject,最後都會執行finally()
console.log('finally');
})
如每次請求傳送完畢,就需要關閉loading提示框,不管請求成功或是失敗,這個loading都需要關閉掉,這時把關閉loading的程式碼寫在finally裡再合適不過了。
Object.fromEntries() 把鍵值對列表轉換為一個物件,此方法和Object.entries() 相對的(逆操作)
let arr = [ ['a', 1], ['b', 2] ];
let obj = Object.fromEntries(arr);
console.log(obj); // {a: 1, b: 2}
Map 轉 Object
let mapObj = new Map();
mapObj.set('name', 'leo');
mapObj.set('age', 18);
console.log(map); // {'name' => 'leo', 'age' => 18}
let newObj = Object.fromEntries(mapObj);
console.log(newObj); // {name: 'leo', age: 18}
語法
let newArr = arr.flat([depth]); // depth可選,指提取巢狀陣列的結構深度,預設值為 1。
flat()
會按照一個可指定的深度遞迴遍歷陣列,並將所有元素與遍歷到的子陣列中的元素合併為一個新陣列返回(陣列扁平化)
let arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat()); // [0, 1, 2, 3, 4]
let arr2 = [0, 1, 2, [[[3, 4]]]];
console.log(arr2.flat(2)); // [0, 1, 2, [3, 4]]
console.log(arr2.flat(3)); // [0, 1, 2, 3, 4]
//使用 Infinity,可展開任意深度的巢狀陣列
let arr3 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr3.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// flat()方法會移除陣列中的空項
let arr5 = [1, 2, , , 4, 5];
arr5.flat(); // [1, 2, 4, 5]
flatMap() 首先使用對映函數對映每個元素,然後將結果壓縮成一個新陣列。顧名思義,它包含兩部分功能一個是 map,一個是 flat(深度為1)
語法
let newArr = arr.flatMap(function callback(currentValue[, index[, array]]) {
// 返回新陣列的元素
}[, thisArg]);
// currentValue,當前正在陣列中處理的元素
// index可選,陣列中正在處理的當前元素的索引
// array可選,被呼叫的 map 陣列
// thisArg,執行callback函數時 使用的this值
舉例
let numbers = [1, 2, 3];
numbers.map(x => [x * 2]); // [[2], [4], [6]]
numbers.flatMap(x => [x * 2]); // [2, 4, 6]
flatMap()
與 map()
和深度depth為1的 flat()
幾乎相同
let arr = ['leo', '', '早上好']
arr.map(s => s.split('')); //[["l", "e", "o"],[""],["早", "上", "好"]]
arr.flatMap(s => s.split('')); // ["l", "e", "o", "", "早", "上", "好"]
trimStart()從字串的開頭刪除空格,trimLeft()是它的別名
let str = ' leo';
console.log(str.length); // 6
str = str.trimStart(); // 或str.trimLeft()
console.log(str.length); // 3
trimEnd()從一個字串的末尾刪除空格,trimRight()是它的別名
let str = 'leo ';
console.log(str.length); // 6
str = str.trimEnd(); // 或str.trimRight()
console.log(str.length); // 3
空值合併操作符( ??
)是一個邏輯操作符,當左側的運算元為 null
或者undefined
時,返回其右側運算元,否則返回左側運算元
let name = undefined ?? "leo"
let age = null ?? 18
console.log(name); // "leo"
console.log(age); // 18
與邏輯或操作符(||
)不同,|| 會在左側運算元為假值(例如 ''、0、
NaN、
false、undefined、null
)時返回右側運算元
let name1 = "" ?? 'leo';
let name2 = "" || 'lion';
console.log(name1); // ""
console.log(name2); // "lion"
let age1 = 0 ?? 18;
let age2 = 0 || 18;
console.log(age1); // 0
console.log(age2); // 18
可選鏈操作符 ( ?.
) 允許讀取位於連線物件鏈深處的屬性的值,而不必明確驗證鏈中的每個參照是否有效。?.
操作符的功能類似於 .
鏈式操作符,不同之處在於,在參照為 null
或者 undefined
的情況下不會引起錯誤,該表示式短路返回值是 undefined
。與函數呼叫一起使用時,如果給定的函數不存在,則返回 undefined
。
當嘗試存取可能不存在的物件屬性時,可選鏈操作符將會使表示式更短、更簡明
舉例
let obj = {
message: {
name: 'leo',
sayHi() {
return 'hi';
}
}
}
在之前的語法中,想獲取到深層屬性或方法,不得不做前置校驗,否則很容易命中 Uncaught TypeError: Cannot read property...
這種錯誤,影響整個程式的執行
let name = obj && obj.message && obj.message.name;
console.log(name); // 'leo'
使用可選鏈操作符 ( ?.
)
let name = obj?.message?.name;
console.log(name); // 'leo'
可選鏈中的 ? 表示如果問號左邊表示式有值, 就會繼續查詢問號後面的欄位。
注意:可選鏈不能用於賦值
let obj = {};
obj?.message?.name = 'leo'; // 報錯
matchAll()
返回一個包含所有匹配正規表示式的結果及分組捕獲組的迭代器
let regexp = /t(e)(st(\d?))/g;
let str = 'test1test2';
let arr = [...str.matchAll(regexp)]; // 返回所有匹配上的
console.log(array[0]); // ["test1", "e", "st1", "1"]
console.log(array[1]); // ["test2", "e", "st2", "2"]
已知Promise.all() 具有並行執行非同步任務的能力。但它的最大問題就是如果其中某個任務出現異常(reject),所有任務都會掛掉,且直接進入reject 狀態。
如頁面上有三個請求,分別請求不同的資料,如果使用Promise.all(),一個介面服務異常,整個都是失敗的,都無法渲染出資料。
我們想要在並行任務中,無論一個任務正常或者異常,都會返回對應的的狀態,則可以使用Promise.allSettled()。
let promise1 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("promise1");
}, 3000);
});
};
let promise2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("promise2");
}, 1000);
});
};
let promise3 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject("error promise3");
}, 2000);
});
};
// 使用Promise.all,則會走到catch裡面,因為promise3是reject
Promise.all([promise1(), promise2(), promise3()])
.then((res) => {
console.log(res);
})
.catch((error) => {
console.log("error", error); // error promise3
});
// 使用Promise.allSettled,不管有沒有錯誤,三個狀態都會返回,不會走到catch
Promise.allSettled([promise1(), promise2(), promise3()])
.then((res) => {
console.log(res);
// [
// {status: 'fulfilled', value: 'promise1'},
// {status: 'fulfilled', value: 'promise2'},
// {status: 'rejected', reason: 'error promise3'}
// ]
})
.catch((error) => {
console.log("error", error);
});
replaceAll()
返回一個新字串,新字串中所有滿足 pattern
的部分都會被替換。pattern
可以是一個字串或一個正規表示式
let str = 'aabbcc'.replaceAll('b', '*');
console.log(str); // 'aa**cc'
使用正規表示式搜尋值時,它必須是全域性的
'aabbcc'.replaceAll(/b/, '*'); // 報錯
修改如下
'aabbcc'.replaceAll(/b/g, '*'); // "aa**cc"
以上幾個特性在本人另一篇部落格已經介紹過
更多相關內容請參考
整理不易,請關注我,點贊、收藏、評論,一起來學前端~