大大提高開發(fā)效率的十個JavaScript技巧
JavaScript 是前端開發(fā)中的必備語言。但是我發(fā)現(xiàn)很多同學對于 JavaScript 的技巧使用卻并不熟悉。所以,今天咱們就來分享一下 JavaScript 的10個好用的技巧,幫你更好地使用 JavaScript,提升開發(fā)效率!
1. 使用 flatMap
有些 JavaScript 方法盡管鮮為人知,但它們解決獨特挑戰(zhàn)的潛力能夠增強編碼效率, 比如 flatMap()
數(shù)組方法 flatMap() 本質上是 map()和 flat() 的組合,區(qū)別在于 flatMap 只能扁平1級,flat 可以指定需要扁平的級數(shù),flatmap 比分別調用這兩個方法稍微高效一些。
- 使用 flat + map
const arr = [1, 2, [4, 5], 6, 7, [8]];
// 使用 map 對每個元素進行操作并用 flat 展平結果
const result = arr.map(element => Array.isArray(element) ? element : [element]).flat();
console.log(result); // output: [1, 2, 4, 5, 6, 7, 8]
- 使用 flatmap
const arr = [1, 2, [4, 5], 6, 7, [8]] ;
console.log(arr.flatMap((element) => element));
// output :[1, 2, 4, 5, 6, 7, 8]
flatmap 盡管是一個方法,但也會有 中間數(shù)組 \(指中間創(chuàng)建了必須進行垃圾收集的臨時數(shù)組\)[1]的產生,flatMap 非常適合在需要靈活性和可讀性的情況下使用。
2. console 的妙用
console 并不只有 console.log(), 實際生產中都會使用已經(jīng)封裝好的log庫,而 控制臺對象 console 實際上內置了許多非常有用的方法,幫助您提高調試輸出的質量和可讀性,掌握它們能使您更輕松地 debug 和修復代碼中的問題。
// 1. console.time 和 console.timeEnd
// 測量執(zhí)行一段代碼所需的時間。識別代碼中的性能瓶頸并對其進行優(yōu)化
console.time('開始獲取數(shù)據(jù)');
fetch('https://reqres.in/api/users')
.then(response => response.json())
.then(data => {
console.timeEnd('獲取數(shù)據(jù)花費時間:');
// ...code
});
// 2. console.dir
// console.dir 方法以分層格式輸出對象的屬性。方便查看對象的結構以及其所有屬性和方法
const promise = new Promise((resolve, reject) => resolve('foo'));
console.dir(promise);
// 3. console.count
// console.count 方法來計算特定日志消息的輸出次數(shù)。這對于跟蹤特定代碼路徑的執(zhí)行次數(shù)以及識別代碼中的熱點非常有用
const fun = (x) => console.count(x);
fun('刻晴'); // 1
fun('甘雨'); // 1
fun('刻晴'); // 2
// 4. console.trace
// trace 可以輸出堆棧跟蹤。對于理解代碼中的執(zhí)行流程以及識別特定日志消息的來源非常有用
const foo = () => console.trace();
const bar = () => foo();
bar();
// 5. console.profile profileEnd
// 測量代碼塊的性能。這對于識別性能瓶頸以及優(yōu)化代碼以提高速度和效率非常有用。
console.profile('MyProfile');
// 想要測量性能的代碼
for (let i = 0; i < 100000; i++) {
// ...code
}
console.profileEnd('MyProfile');
3. 深拷貝 structuredClone()
此前,如果開發(fā)人員想要深拷貝對象,經(jīng)常需要依賴第三方庫來實現(xiàn)或者手動實現(xiàn)一個神拷貝,或者采取 const cloneObj = JSON.parse(JSON.stringify(obj)); 的 hack, 但其在處理包含循環(huán)引用或不符合 JSON 的數(shù)據(jù)類型(如 Map 和 Set,Blob 等 ) 的更復雜對象時,是有很多不足之處的
而現(xiàn)在,JavaScript 內置了一個 structuredClone() 的方法, 此方法提供了一種簡單有效的方法來深度克隆對象, 且適用于大多數(shù)現(xiàn)代瀏覽器和 Node.js v17 以上
// 將原始對象傳遞給該函數(shù), 它將返回一個具有不同引用和對象屬性引用的深層副本
const obj = { name: 'Mike', friends: [{ name: 'Sam' }] };
const clonedObj = structuredClone(obj);
console.log(obj.name === clonedObj); // false
console.log(obj.friends === clonedObj.friends); // false
與眾所周知的 JSON.parse(JSON.stringify())” 不同, structuredClone() 允許您克隆循環(huán)引用,這是目前在 JavaScript 中使用深拷貝最簡單的方法。
4. 帶標簽的模板
帶標簽的模板(Tagged\_Templates[2]) - 是模板字符串(反引號)的一種更高級的形式,它允許你使用函數(shù)解析模板字面量。
這個高級特性我也是在 Next.js 14[3] 發(fā)布后人們都在討論的一張圖才去了解的??,盡管這個特性是 ES6 就有的,至今已有8年!?。〉腋掖蛸€知道這個并使用過這個特性的人屈指可數(shù)。
相信許多人已經(jīng)見過下圖(因為這個知識點請停止嘲笑 ??Next.js 14), 相信許多人的第一反應就是回到二十年前 PHP 時代并且代碼容易遭受 sql 注入攻擊 , 但實際上是安全的。這得益于模板字符串的高級特性 - ( 帶標簽的模板 \-Tagged\_Templates[4])
如果你不理解 Tagged_Templates 如何工作, 那么就讓我用一個例子來簡單說明下吧:
const checkCurrency = function (currency, amount) {
const symbol = currency[0] === "USD" ? "$" : "¥";
console.log(currency[0], "--" ,currency[1])// Outputs: USD -- RMB
return `${symbol}${amount}`;
};
const amount = 200;
const currency = checkCurrency`USD${amount}RMB`;
console.log(currency); // Outputs: $200
// 1. checkCurrency是一個函數(shù),標簽函數(shù)的第一個參數(shù)currency包含一個字符串值數(shù)組
// 2. 字符串數(shù)組由標簽模板里的字符串組成,在`USD${amount}RMB`里,字符串有USD和RMB
// 3. 因此 currency[0] 為第一個字符串 USD, currency[1] 則是第二個字符串 RMB
// 3. checkCurrency函數(shù)的其余參數(shù)則根據(jù)表達式直接插入到字符串中,如 amount = 200
// 4. 在checkCurrency函數(shù)的內部,判斷第一個參數(shù)數(shù)組首項是否是‘USD’,是則選擇"$"符號,否則是 "¥"
// 5. 函數(shù)內部將symbol和amount結合在一起返回一個新的字符串,symbol代表貨幣符號,而amount代表傳遞給函數(shù)的金額。
// 6. 返回的字符串賦值給 currency 常量, 因此 log為 $200
可以看到,Tagged Templates 的工作方式是將模板字符串里的所有字符串作為一個數(shù)組傳遞給函數(shù)的第一個參數(shù),其余參數(shù)則根據(jù)相應的表達式直接插入到字符串中,Tagged Templates將 文字字符串 和表達式的結果 傳遞給函數(shù),然后該函數(shù)可以以自定義方式操作并返回它們。這樣開發(fā)者在構建 SQL 查詢時,對輸入進行適當?shù)霓D義和驗證,從而避免 SQL 注入攻擊。
帶標簽的模板字符串可用于很多用途,例如 安全性、i18n和本地化 等。
5. 使用Symbols作為WeakMap的鍵
WeakMap 和 Map 很像,但不同點在于它的鍵(key) 只能是對象 Objects 和 symbol,這些鍵被作為**弱引用存儲(weakly)**。
為什么?因為 WeakMap 的鍵必須是可垃圾回收的。大多數(shù)原始數(shù)據(jù)類型可以任意創(chuàng)建并且沒有生命周期,因此它們不能用作鍵, 而 對象Objects 和 non-registered symbols 可以用作鍵,因為它們是垃圾可收集的 - MDN- WeakMap[5]。
這個特性意味著除了鍵之外內存中沒有其他對對象的引用,JavaScript 引擎可以在需要時對對象執(zhí)行垃圾回收。
// map
let user = { name: "User" };
let map = new Map();
map.set(user, "刻晴");
user = null; // 置null來覆蓋引用,'user'被存在 map 的內部,通過 map.keys() 獲取
// WeakMap
let user = { name: "User" };
let weakMap = new WeakMap();
weakMap.set(user, "甘雨");
user = null; // 使用 WeakMap,'user' 已經(jīng)被從內存中刪除
好了,那 WeakMap 到底有什么作用呢?根據(jù)其特點可以聯(lián)想到 WeakMap 的用途可以是自定義緩存以及檢測內存泄漏。
通過使用對象作為鍵,您可以將緩存的值與特定對象相關聯(lián)。當對象被垃圾收集時,相應的 WeakMap 條目將被自動刪除,立即清除緩存。
在 ES14 中, 使用 symbol 作為 WeakMap 的 key 已經(jīng)成為可能, 這可以使鍵值對在 WeakMap 中扮演的角色更加清晰。因為唯一能在 WeakMap 中被作為 key 使用的原始類型只有 symbol, symbol 能保證 key 是 唯一的并且無法重新創(chuàng)建。
let mySymbol = Symbol('mySymbol');
let myWeakMap = new WeakMap();
let obj = {
name: '寫前端的刻貓貓'
};
myWeakMap.set(mySymbol, obj);
console.log(myWeakMap.get(mySymbol)); // Output: {name: '寫前端的刻貓貓'}
6. 充分使用 generator
生成器 (Generator) 和 迭代器 (iterators) 可能是 JavaScript 開發(fā)人員最不常使用的代碼,其知識僅限于編碼面試。(因為有更好用的語法糖 async/await ???)
?
生成器 (Generator) 是控制異步編程、生成可迭代對象和生成多個值的強大方法。生成器與傳統(tǒng)函數(shù)不同。他們可以多次啟動和停止執(zhí)行。這使它們能夠產生大量值并在以后繼續(xù)執(zhí)行,從而使它們非常適合管理異步操作、構造迭代器和處理無盡的數(shù)據(jù)流。
試想一下,假如在一個獲取數(shù)據(jù)的場景下,數(shù)據(jù)庫/ API 的數(shù)據(jù)量可能是無限的,而你必須將它們傳輸?shù)角岸?,你會怎么做呢?/p>
這種情況下, react 中最常用的方案就是無限加載方案, 如果是在 node 中或者原生JS,你該如何實現(xiàn)無限加載之類的功能。
async function *fetchProducts(){
while (true){
const productUrl = "https://fakestoreapi.com/products?limit=2";
const res = await fetch(productUrl)
const data = await res.json()
yield data;
// 在這里操作用戶界面
// 或將其保存在數(shù)據(jù)庫或其他地方
// 將其用作副作用的地方
// 即使某些條件匹配,也中斷流程
}
}
async function main() {
const itr = fetchProducts();
// 這應該根據(jù)用戶交互來調用
// 或者其他技巧,因為您不希望出現(xiàn)無限加載。
console.log( await itr.next() );
}
return main()
這就是 迭代器 (iterators) 真正有用的地方,而不是將請求的大量數(shù)據(jù)流式傳輸?shù)奖镜卮鎯蛘吣承┪恢谩_@是使用 異步生成器(async generators) 執(zhí)行此操作的這樣之一, 這樣我們就可以解決JS中的無限加載問題。
7. 私有類字段
現(xiàn)在,JavaScript類支持使用#符號的私有字段。
私有字段不能從類外部訪問,從而提供封裝和信息隱藏。
class Counter {
#count = 0;
increment() {
this.#count++;
}
getCount() {
return this.#count;
}
}
const counter = new Counter();
counter.increment();
console.log(counter.getCount()); // 1
8. Promise.allSettled()
Promise.allSettled() 方法返回一個 Promise,該 Promise 在所有給定的 Promise 已經(jīng) resolve 或 reject 后 resolve,提供每個 Promise 的結果數(shù)組。
const promises = [
Promise.resolve('Resolved'),
Promise.reject('Rejected')
];
Promise.allSettled(promises)
.then(results => {
console.log(results);
});
// [{ status: "fulfilled", value: "Resolved" }, { status: "rejected", reason: "Rejected" }]
9. globalThis 全局對象
globalThis對象提供了一種在不同環(huán)境下(包括瀏覽器和Node.js)訪問全局對象的一致方式。
console.log(globalThis === window); // 在瀏覽器場景下: true
console.log(globalThis === global); // 在 Node.js 中: outputs: true
10. 代理
代理對象允許你為基本對象操作創(chuàng)建自定義行為。
它允許截獲和修改對象操作,例如訪問屬性、賦值和調用方法。
const handler = {
get: function (obj, prop) {
return prop in obj ? obj[prop] : 37;
},
};
const p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;
console.log(p.a, p.b); // 1, undefined
console.log("c" in p, p.c); // false, 37
Reference
[1]https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap#description: https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FJavaScript%2FReference%2FGlobal_Objects%2FArray%2FflatMap%23description
[2]https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates: https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FJavaScript%2FReference%2FTemplate_literals%23tagged_templates
[3]https://nextjs.org/: https://link.juejin.cn?target=https%3A%2F%2Fnextjs.org%2F
[4]https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates: https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FJavaScript%2FReference%2FTemplate_literals%23tagged_templates
[5]https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap: https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FJavaScript%2FReference%2FGlobal_Objects%2FWeakMap