7 個(gè)我最喜歡的 JavaScript 小技巧
英文 |?https://medium.com/young-coder/7-of-my-favorite-little-javascript-tricks-4f2a1cfe68b4
翻譯 | 楊小愛(ài)
// Create three constants to use as an enumconst TrafficLight = {Green: Symbol('green'),Red: Symbol('red'),Yellow: Symbol('yellow')}// This function uses the enumfunction switchLight(newLight) {if (newLight === TrafficLight.Green) {console.log('Turning light green');}else if (newLight === TrafficLight.Yellow) {console.log('Get ready to stop');}else {console.log('Turning light red');}return newLight;}// Let's try it out!let light = TrafficLight.Green;light = switchLight(TrafficLight.Yellow);light = switchLight(TrafficLight.Red);console.log(light); // shows "Symbol('red')"
在此示例中,每個(gè)枚舉值(例如,TrafficLight.Green)都獲得一個(gè)唯一值。但是你永遠(yuǎn)不會(huì)真正看到那個(gè)值,因?yàn)?Symbol 讓它完全不透明。
因此,如果您需要在應(yīng)用程序之外序列化此數(shù)據(jù)(例如,將其存儲(chǔ)在磁盤上或通過(guò)網(wǎng)絡(luò)發(fā)送),這可能不是您想要的方法。
2、在控制臺(tái)中無(wú)痛地測(cè)試代碼
引導(dǎo) JavaScript 測(cè)試頁(yè)面只需要幾秒鐘。但有時(shí)我想嘗試一個(gè)單獨(dú)的、離散的 JavaScript 函數(shù)。如果我可以在瀏覽器中處理我正在閱讀的文章旁邊的這個(gè)測(cè)試代碼片段,那就更有用了。
現(xiàn)在,調(diào)用瀏覽器的 DevTools(Windows 上為 F12,macOS 上為 Cmd-Shift-J 或 Cmd-Option-J,具體取決于瀏覽器)并沒(méi)有什么神奇之處。
在 JavaScript 控制臺(tái)中輸入一些代碼并沒(méi)有什么神奇之處——只需記住在每個(gè)換行符處按 Shift+Enter 并按 Enter 以運(yùn)行完成的代碼。
但是,如果你想迭代一個(gè)例子(輸入一次,編輯它,重新運(yùn)行它,等等),你需要控制你的代碼執(zhí)行的范圍。
最好的方法是用大括號(hào) { } 將整個(gè)代碼塊括起來(lái)。這樣你就可以運(yùn)行你的代碼(按 Enter),再次調(diào)用它(按向上箭頭),編輯它,然后重新運(yùn)行它,所有這些都不會(huì)出現(xiàn)惱人的“標(biāo)識(shí)符已經(jīng)聲明”錯(cuò)誤。
所以不要輸入這個(gè):
let testValue = 40+12;console.log(testValue);
你要這樣寫:
{let testValue = 40+12;console.log(testValue);}
3、一行深度復(fù)制一個(gè)數(shù)組
您可能知道現(xiàn)代 JavaScript 的一項(xiàng)重大改進(jìn)是一組函數(shù)式數(shù)組處理方法,它們使您無(wú)需迭代即可處理數(shù)據(jù)。
這些方法中最強(qiáng)大的方法之一是 Array.map(),它對(duì)每個(gè)元素運(yùn)行一個(gè)函數(shù),并為您提供一個(gè)帶有結(jié)果的新數(shù)組。
Array.map() 可以做很多技巧,但克隆數(shù)組是更有用的技巧之一。要了解它是如何工作的,請(qǐng)想象您創(chuàng)建了一個(gè)這樣的數(shù)組,其中包含兩個(gè)對(duì)象:
const objectsOriginal = [{name: 'Sadie', age: 12},{name: 'Patrick', age: 18}];
現(xiàn)在假設(shè)您要復(fù)制這些對(duì)象。這段代碼不是你想要的:
// All this gets you is two variables pointing to the same arrayconst objectsCopy = objectsOriginal;
這有點(diǎn)好,但仍然沒(méi)有做你想要的:
// Creates two array objects, but they share the same people objectsconst objectsCopy = [...objectsOriginal];
(您可以通過(guò)更改一個(gè)數(shù)組中的對(duì)象并驗(yàn)證它是同一個(gè)更改的對(duì)象來(lái)測(cè)試這一點(diǎn),即使您通過(guò)另一個(gè)數(shù)組訪問(wèn)它也是如此。)
現(xiàn)在這是一個(gè)使用 Array.map() 的解決方案,它接受每個(gè)元素,擴(kuò)展對(duì)象,然后創(chuàng)建一個(gè)具有相同屬性的重復(fù)對(duì)象:
const objectsCopy = objectsOriginal.map(element => ({...element}));這是該技術(shù)的完整演示如下:
const objectsOriginal = [{name: 'Sadie', age: 12},{name: 'Patrick', age: 18}];// Create a new array with copied objectsconst objectsCopy = objectsOriginal.map( element => ({...element}) );// Change one of the people objects in objectsCopyobjectsCopy[0].age = 14;// Investigate the same object in objectsOriginalconsole.log(objectsOriginal[0].age); // 12
當(dāng)然,也有一些注意事項(xiàng)。這是一個(gè)單層深拷貝,所以如果你的對(duì)象持有對(duì)其他對(duì)象的引用,它們就不會(huì)被復(fù)制。
在這種情況下,最好通過(guò)創(chuàng)建自定義類并編寫自定義 clone() 方法來(lái)形式化克隆邏輯。
然后你可以使用 Array.map() 來(lái)調(diào)用 clone() 方法:
const objectsCopy = objectsOriginal.map(element => element.clone());4、一行清空一個(gè)數(shù)組
如果我們討論數(shù)組,這里我分享一個(gè)有用的技巧。有時(shí)你想清空一個(gè)數(shù)組對(duì)象而不用一個(gè)新的空白數(shù)組替換它(可能是因?yàn)樗涣硪粋€(gè)對(duì)象引用)。
在開(kāi)始迭代和調(diào)用 Array.remove() 之前,這里有一個(gè)通過(guò)設(shè)置 length 屬性起作用的快捷方式:
const numbers = [2, 42, 5, 304, 1, 13];numbers.length = 0;// The array is now empty
如果您學(xué)習(xí)的是傳統(tǒng)的 OOP 語(yǔ)言,這可能看起來(lái)很奇怪,因?yàn)?Array.length 似乎是一個(gè)應(yīng)該只讀的屬性,并且設(shè)置屬性通常不應(yīng)該觸發(fā)操作(如刪除元素)。但是在 JavaScript 中,有時(shí)您只是做感覺(jué)良好的事情。
5、給你的對(duì)象一個(gè)合理的字符串表示
是否厭倦了在使用 console.log() 時(shí),在瀏覽器控制臺(tái)中看到“[object Object]”?您可以通過(guò)為您的對(duì)象提供一個(gè)可觀的 toString() 方法來(lái)輕松覆蓋此行為。下面是一個(gè)例子:
class Person {constructor(firstName, lastName) {this.firstName = firstName;this.lastName = lastName;}toString() {return `${this.lastName}, ${this.firstName}`;}}// Let's use our Person classconst newPerson = new Person('Luke', 'Takei');const message = 'The name is ' + newPerson;// Now message = 'The name is Takei, Luke'// which is much better than 'The name is [object Object]'
再一次,JavaScript 剝離了您在經(jīng)典 OOP 語(yǔ)言中看到的一些基礎(chǔ)設(shè)施。(例如,沒(méi)有覆蓋關(guān)鍵字。)但它有效。
6、支持類中的方法鏈
方法鏈并不是真正的技巧,但它是我們并不總是認(rèn)為支持的那些實(shí)踐之一,它可以為您節(jié)省一些時(shí)間。
同樣重要的是,它與 JavaScript 的生活方式相契合,因?yàn)樵S多內(nèi)置對(duì)象使用它取得了良好的效果。
考慮這個(gè)帶有 Array 對(duì)象的例子。在這里,方法鏈允許您將兩個(gè)操作合并為一行——數(shù)組連接和數(shù)組排序:
const evens = [2, 4, 6, 8];const odds = [1, 3, 5, 7, 9];const evensAndOdds = evens.concat(odds).sort();console.log(evensAndOdds); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
在您自己的自定義類中支持方法鏈的最簡(jiǎn)單方法就是使用 this 關(guān)鍵字返回對(duì)當(dāng)前對(duì)象的引用。這個(gè) Book 類在它的 raisePrice() 和 releaseNewEdition() 方法中使用了這種技術(shù):
class Book {constructor(title, author, price, publishedDate) {this.title = title;this.author = author;this.price = price;this.publishedDate = publishedDate;}raisePrice(percent) {const increase = this.price*percent;this.price += Math.round(increase)/100;return this;}releaseNewEdition() {// Set the pulishedDate to todaythis.publishedDate = new Date();return this;}}// Let's make a Book objectconst book = new Book('I Love Mathematics', 'Adam Up', 15.99,new Date(2010, 2, 2));// Raise the price 15% and then change the edition, using method chainingconsole.log(book.raisePrice(15).releaseNewEdition());
函數(shù)式程序員會(huì)看到這個(gè),說(shuō)也許您根本不想要一個(gè)有狀態(tài)的對(duì)象,而是一個(gè)不斷返回副本的不可變對(duì)象,就像 Array.concat() 和 Array.sort() 做的那樣。
如果這種方法對(duì)您的代碼庫(kù)有意義,只需在方法末尾返回一個(gè)帶有修改細(xì)節(jié)的新對(duì)象,而不是當(dāng)前實(shí)例,如下所示:
raisePrice(percent) {const increase = this.price*percent;return new Book(this.title, this.author,this.price + Math.round(increase)/100, this.date);}
7、制作可重復(fù)的隨機(jī)數(shù)列表
這個(gè)更專業(yè)一點(diǎn),但它在緊要關(guān)頭對(duì)我很有用。
有幾種不同的方法可以在 JavaScript 中創(chuàng)建偽隨機(jī)數(shù)。
標(biāo)準(zhǔn) Math.random() 獲取不加密安全的隨機(jī)值,這適用于大多數(shù)用途。如果沒(méi)有,那么鮮為人知的 Crypto.getRandomValues() 可以幫助您。
但是,這兩種方法都為您提供了不可重復(fù)的隨機(jī)數(shù)。
如果您想運(yùn)行可重復(fù)的測(cè)試或模擬,這不是您所需要的,這對(duì)于大量統(tǒng)計(jì)和科學(xué)操作很重要。
這個(gè)領(lǐng)域變得非常深入和復(fù)雜,但我總是保持簡(jiǎn)單而快速的 Mulberry32 算法來(lái)給我完全確定性的偽隨機(jī)數(shù)(這意味著如果你從相同的種子開(kāi)始,你總是得到相同的列表值)。
我將它封裝在一個(gè)生成器函數(shù)中,這是我最喜歡的 JavaScript 專用特性之一。
這是代碼:
function* mulberry32(seed) {let t = seed += 0x6D2B79F5;// Generate numbers indefinitelywhile(true) {t = Math.imul(t ^ t >>> 15, t | 1);t ^= t + Math.imul(t ^ t >>> 7, t | 61);yield ((t ^ t >>> 14) >>> 0) / 4294967296;}}
你不需要理解這個(gè)實(shí)現(xiàn)的位移部分,它是從經(jīng)典的 C 實(shí)現(xiàn)中借來(lái)的。JavaScript 的不同之處在于,這是一個(gè)生成器函數(shù),正如 function* 關(guān)鍵字中的星號(hào)所表示的那樣。
生成器函數(shù)使用 yield 返回按需值 — 在本例中為隨機(jī)數(shù)。
以下是您創(chuàng)建和調(diào)用生成器的方法:
// Use the same seed to get the same sequenceconst seed = 98345;const generator = mulberry32(seed);console.log(generator.next().value); // 0.9057375795673579console.log(generator.next().value); // 0.7620641703251749console.log(generator.next().value); // 0.0211441791616380
這個(gè)生成器函數(shù)包裝了一個(gè)無(wú)限循環(huán),只要你繼續(xù)調(diào)用 next() 就會(huì)運(yùn)行。如果您不需要隨機(jī)數(shù),則生成器的執(zhí)行將暫停,其所有狀態(tài)保持不變。
當(dāng)然,您不需要生成器函數(shù)來(lái)創(chuàng)建隨機(jī)數(shù)列表,但它是一個(gè)優(yōu)雅的解決方案。
總結(jié)
如果您喜歡這些示例,請(qǐng)查看注意收藏學(xué)習(xí),在此,非常感謝您的閱讀,祝您編程愉快!
如果您覺(jué)得這些內(nèi)容對(duì)您有幫助,請(qǐng)記得關(guān)注我,點(diǎn)贊我,并將今天的內(nèi)容分享給您的朋友,也許能夠幫助到他。
學(xué)習(xí)更多技能
請(qǐng)點(diǎn)擊下方公眾號(hào)
![]()

