分享 7 個你可能喜歡的 JS 小技巧

| https://medium.com/young-coder/7-of-my-favorite-little-javascript-tricks-4f2a1cfe68b4
翻譯 | 楊小愛
// 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')"
在此示例中,每個枚舉值(例如,TrafficLight.Green)都獲得一個唯一值。但是你永遠(yuǎn)不會真正看到那個值,因為 Symbol 讓它完全不透明。
因此,如果您需要在應(yīng)用程序之外序列化此數(shù)據(jù)(例如,將其存儲在磁盤上或通過網(wǎng)絡(luò)發(fā)送),這可能不是您想要的方法。
2、在控制臺中無痛地測試代碼
引導(dǎo) JavaScript 測試頁面只需要幾秒鐘。但有時我想嘗試一個單獨的、離散的 JavaScript 函數(shù)。如果我可以在瀏覽器中處理我正在閱讀的文章旁邊的這個測試代碼片段,那就更有用了。
現(xiàn)在,調(diào)用瀏覽器的 DevTools(Windows 上為 F12,macOS 上為 Cmd-Shift-J 或 Cmd-Option-J,具體取決于瀏覽器)并沒有什么神奇之處。
在 JavaScript 控制臺中輸入一些代碼并沒有什么神奇之處——只需記住在每個換行符處按 Shift+Enter 并按 Enter 以運行完成的代碼。
但是,如果你想迭代一個例子(輸入一次,編輯它,重新運行它,等等),你需要控制你的代碼執(zhí)行的范圍。
最好的方法是用大括號 { } 將整個代碼塊括起來。這樣你就可以運行你的代碼(按 Enter),再次調(diào)用它(按向上箭頭),編輯它,然后重新運行它,所有這些都不會出現(xiàn)惱人的“標(biāo)識符已經(jīng)聲明”錯誤。
所以不要輸入這個:
let testValue = 40+12;console.log(testValue);
你要這樣寫:
{let testValue = 40+12;console.log(testValue);}
3、一行深度復(fù)制一個數(shù)組
您可能知道現(xiàn)代 JavaScript 的一項重大改進(jìn)是一組函數(shù)式數(shù)組處理方法,它們使您無需迭代即可處理數(shù)據(jù)。
這些方法中最強(qiáng)大的方法之一是 Array.map(),它對每個元素運行一個函數(shù),并為您提供一個帶有結(jié)果的新數(shù)組。
Array.map() 可以做很多技巧,但克隆數(shù)組是更有用的技巧之一。要了解它是如何工作的,請想象您創(chuàng)建了一個這樣的數(shù)組,其中包含兩個對象:
const objectsOriginal = [{name: 'Sadie', age: 12},{name: 'Patrick', age: 18}];
現(xiàn)在假設(shè)您要復(fù)制這些對象。這段代碼不是你想要的:
// All this gets you is two variables pointing to the same arrayconst objectsCopy = objectsOriginal;
這有點好,但仍然沒有做你想要的:
// Creates two array objects, but they share the same people objectsconst objectsCopy = [...objectsOriginal];
(您可以通過更改一個數(shù)組中的對象并驗證它是同一個更改的對象來測試這一點,即使您通過另一個數(shù)組訪問它也是如此。)
現(xiàn)在這是一個使用 Array.map() 的解決方案,它接受每個元素,擴(kuò)展對象,然后創(chuàng)建一個具有相同屬性的重復(fù)對象:
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)然,也有一些注意事項。這是一個單層深拷貝,所以如果你的對象持有對其他對象的引用,它們就不會被復(fù)制。
在這種情況下,最好通過創(chuàng)建自定義類并編寫自定義 clone() 方法來形式化克隆邏輯。
然后你可以使用 Array.map() 來調(diào)用 clone() 方法:
const objectsCopy = objectsOriginal.map(element => element.clone());4、一行清空一個數(shù)組
如果我們討論數(shù)組,這里我分享一個有用的技巧。有時你想清空一個數(shù)組對象而不用一個新的空白數(shù)組替換它(可能是因為它被另一個對象引用)。
在開始迭代和調(diào)用 Array.remove() 之前,這里有一個通過設(shè)置 length 屬性起作用的快捷方式:
const numbers = [2, 42, 5, 304, 1, 13];numbers.length = 0;// The array is now empty
如果您學(xué)習(xí)的是傳統(tǒng)的 OOP 語言,這可能看起來很奇怪,因為 Array.length 似乎是一個應(yīng)該只讀的屬性,并且設(shè)置屬性通常不應(yīng)該觸發(fā)操作(如刪除元素)。但是在 JavaScript 中,有時您只是做感覺良好的事情。
5、給你的對象一個合理的字符串表示
是否厭倦了在使用 console.log() 時,在瀏覽器控制臺中看到“[object Object]”?您可以通過為您的對象提供一個可觀的 toString() 方法來輕松覆蓋此行為。下面是一個例子:
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 語言中看到的一些基礎(chǔ)設(shè)施。(例如,沒有覆蓋關(guān)鍵字。)但它有效。
6、支持類中的方法鏈
方法鏈并不是真正的技巧,但它是我們并不總是認(rèn)為支持的那些實踐之一,它可以為您節(jié)省一些時間。
同樣重要的是,它與 JavaScript 的生活方式相契合,因為許多內(nèi)置對象使用它取得了良好的效果。
考慮這個帶有 Array 對象的例子。在這里,方法鏈允許您將兩個操作合并為一行——數(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]
在您自己的自定義類中支持方法鏈的最簡單方法就是使用 this 關(guān)鍵字返回對當(dāng)前對象的引用。這個 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ù)式程序員會看到這個,說也許您根本不想要一個有狀態(tài)的對象,而是一個不斷返回副本的不可變對象,就像 Array.concat() 和 Array.sort() 做的那樣。
如果這種方法對您的代碼庫有意義,只需在方法末尾返回一個帶有修改細(xì)節(jié)的新對象,而不是當(dāng)前實例,如下所示:
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ù)列表
這個更專業(yè)一點,但它在緊要關(guān)頭對我很有用。
有幾種不同的方法可以在 JavaScript 中創(chuàng)建偽隨機(jī)數(shù)。
標(biāo)準(zhǔn) Math.random() 獲取不加密安全的隨機(jī)值,這適用于大多數(shù)用途。如果沒有,那么鮮為人知的 Crypto.getRandomValues() 可以幫助您。
但是,這兩種方法都為您提供了不可重復(fù)的隨機(jī)數(shù)。
如果您想運行可重復(fù)的測試或模擬,這不是您所需要的,這對于大量統(tǒng)計和科學(xué)操作很重要。
這個領(lǐng)域變得非常深入和復(fù)雜,但我總是保持簡單而快速的 Mulberry32 算法來給我完全確定性的偽隨機(jī)數(shù)(這意味著如果你從相同的種子開始,你總是得到相同的列表值)。
我將它封裝在一個生成器函數(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;}}
你不需要理解這個實現(xiàn)的位移部分,它是從經(jīng)典的 C 實現(xiàn)中借來的。JavaScript 的不同之處在于,這是一個生成器函數(shù),正如 function* 關(guān)鍵字中的星號所表示的那樣。
生成器函數(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
這個生成器函數(shù)包裝了一個無限循環(huán),只要你繼續(xù)調(diào)用 next() 就會運行。如果您不需要隨機(jī)數(shù),則生成器的執(zhí)行將暫停,其所有狀態(tài)保持不變。
當(dāng)然,您不需要生成器函數(shù)來創(chuàng)建隨機(jī)數(shù)列表,但它是一個優(yōu)雅的解決方案。
總結(jié)
如果您喜歡這些示例,請查看注意收藏學(xué)習(xí),在此,非常感謝您的閱讀,祝您編程愉快!
如果您覺得這些內(nèi)容對您有幫助,請記得關(guān)注我,點贊我,并將今天的內(nèi)容分享給您的朋友,也許能夠幫助到他。
學(xué)習(xí)更多技能
請點擊下方公眾號
![]()
