7個提高JavaScript代碼質量的優(yōu)秀實踐

原文標題:7 JavaScript Best Practices to Improve Code Quality
作者:Dhruv Bhanushali
翻譯 | http://developer.51cto.com/art/202105/661081.htm
1、塊作用域聲明(Block Scoped Declarations)
var x = 10if (true) {var x = 15 // inner declaration overrides declaration in parent scopeconsole.log(x) // prints 15}console.log(x) // prints 15
由于已定義的變量var并非塊作用域(block-scoped),因此如果在小作用域內被重新定義,它們就會影響到外部作用域的值。
然而,如果我們用let和const兩個新的關鍵字來替換var,便可以避免該缺陷(請參見如下代碼段)。
let y = 10if (true) {let y = 15 // inner declaration is scoped within the if blockconsole.log(y) // prints 15}console.log(y) // prints 10
當然,const與let在語義上有所不同,那些用const聲明的變量,無法在其作用域內被重新分配(如下代碼段所示)。不過,這并不意味著它們是不可改變的,而只是代表著它們的引用不能被更改。
const x = []x.push("Hello", "World!")x // ["Hello", "World!"]x = [] // TypeError: Attempted to assign to readonly property.
2、箭頭函數(Arrow Functions)
作為新近被引入JavaScript的一項重要功能,箭頭函數具有許多優(yōu)點。首先,它們能夠讓JavaScript的函數看起來更加整潔,并且更便于開發(fā)者的編寫。
let x = [1, 2, 3, 4]x.map(val => val * 2) // [2, 4, 6, 8]x.filter(val => val % 2 == 0) // [2, 4]x.reduce((acc, val) => acc + val, 0) // 10
如上述示例所示,“=>”后面的函數以一種簡潔的語法,替換了傳統(tǒng)函數。
如果函數主體是單個表達式,則已經隱含了作用域括號{}和return關鍵字,所以無需額外寫入。
如果函數只有一個參數,則也已經隱含了參數括號(),同樣無需額外寫入。
如果函數體的表達式是一套字典(dictionary),則必須將其括入括號()中。
箭頭函數的另一個優(yōu)勢在于:為了避免由于使用this關鍵字,而引起的諸多不便,箭頭函數并不會定義作用域,而是會存在于其父作用域中。也就是說,箭頭函數沒有任何針對this的綁定。
在箭頭函數中,this的值與父作用域中的值是相同的。因此,箭頭函數不能被用作各種方法或構造函數。它們既不適用于apply、bind或call,也沒有針對super的綁定。
此外,箭頭函數還會受到諸如:缺少可供傳統(tǒng)功能訪問的arguments對象,以及缺少函數體中的yield等其他限制。
可以說,箭頭函數并非是與標準函數的1:1替代,而是向JavaScript中添加了額外的功能集。
3、可選鏈(Optional Chaining)
讓我們來試想一個類似person對象的、具有深層嵌套的數據結構。業(yè)務應用需要訪問到該對象的名字和姓氏。由此,我們可以編寫出如下JavaScript代碼:
public class HelloWorld {public static void main(String[] args) {System.out.println("Hello World");}}
然而,如果person對象并不包含嵌套的name對象,則可能會出現如下錯誤。
person = {age: 42}person.name.first // TypeError: Cannot read property 'first' of undefinedperson.name.last // TypeError: Cannot read property 'last' of undefined
對此,開發(fā)人員往往需要通過如下代碼,來予以解決。顯然,這些代碼不但冗長難寫,而且可讀性較差。
person && person.name && person.name.first // undefined
而作為JavaScript的一項新功能,可選鏈的語法允許您訪問嵌套得更深的對象屬性,而不用擔心屬性是否真的存在。
也就是說,如果可選鏈在挖掘過程遇到了null或undefined的值,那么就會通過短路(short-circuit)計算,返回undefined,而不會報錯。
person?.name?.first // undefined
如上述代碼所示,其結果代碼簡潔且明了。
4、空值合并(Null-ish Coalescing)
在引入空值合并運算符之前,在輸入為空的情況下,JavaScript開發(fā)人員需要使用OR運算符--||,以回退到默認值。這就會導致:即使出現合法的、但屬于虛假值(falsy values),也會被回退到默認值的情況。
function print(val) {return val || 'Missing'}print(undefined) // 'Missing'print(null) // 'Missing'print(0) // 'Missing'print('') // 'Missing'print(false) // 'Missing'print(NaN) // 'Missing'
如今,JavaScript推出了null合并運算符--??。它能夠保證只有在前面的表達式為null-ish的情況下,才會觸發(fā)回退。值得注意的是,此處的空值是指null或undefined。
function print(val) {return val ?? 'Missing'}print(undefined) // 'Missing'print(null) // 'Missing'print(0) // 0print('') // ''print(false) // falseprint(NaN) // NaN
如此,您可以確保自己的程序能夠接受虛假值作為合法輸入,而不會最終被回退。
5、邏輯賦值(Logical Assignment)
假設您需要先判斷是否為空,再為變量分配數值,那么如下代碼便展示了這樣的基本邏輯:
if (x === null || x == undefined) {x = y}
如果您熟悉上面提到的短路計算的工作原理,則可能會使用null-ish合并運算符(coalescing operator),將上述三行代碼替換為如下更簡潔的版本。
x ?? (x = y) // x = y if x is nullish, else no effect
由上述代碼可知,如果x為null-ish的話,我們可以使用null-ish合并運算符的短路功能,來執(zhí)行第二部分(x = y)。這段代碼雖然非常簡潔,但是不太容易被閱讀或理解。而我們完全可以使用如下代碼,根據邏輯上的null-ish分配,來消除此類變通方法。
x ??= y // x = y if x is nullish, else no effect
同樣,JavaScript還引入了邏輯AND賦值--&&=、邏輯OR賦值--||=的運算符。這些運算符僅在滿足特定條件時被執(zhí)行賦值,否則并不起作用。
x ||= y // x = y if x is falsy, else no effectx &&= y // x = y if x is truthy, else no effect
專家建議:如果您有過Ruby的編程經驗,那么您會一眼識別出||=和&&=運算符。畢竟Ruby并沒有虛假值的概念。
6、已命名捕獲組(Named Capture Groups)
不知您是否知曉正則表達式中的“捕獲組”的相關概念?如下面的代碼段所示,它是與括號中的正則表達式部分匹配的字符串。
let re = /(\d{4})-(\d{2})-(\d{2})/let result = re.exec('Pi day this year falls on 2021-03-14!')result[0] // '2020-03-14', the complete matchresult[1] // '2020', the first capture groupresult[2] // '03', the second capture groupresult[3] // '14', the third capture group
一直以來,正則表達式都能夠支持已命名捕獲組。這是一種通過引用名稱、而非索引,來捕獲各個組的方式。
目前,在ES9中,該功能已被JavaScript實現。正如下面的代碼段所示,其結果對象包含了一個嵌套的組對象,其中每個捕獲組的值都能夠映射到其名稱上。
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/let result = re.exec('Pi day this year falls on 2021-03-14!')result.groups.year // '2020', the group named 'year'result.groups.month // '03', the group named 'month'result.groups.day // '14', the group named 'day'
而且,新的API與JavaScript的解構分配功能,也能夠完美地結合在一起(請參見下面的代碼段)。
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/let result = re.exec('Pi day this year falls on 2021-03-14!')let { year, month, day } = result.groupsyear // '2020'month // '03'day // '14'
7、async和await
眾所周知,異步性是JavaScript的一項強大功能。許多可能長時間運行、或較為耗時的函數,能夠返回Promise,而不會被阻止運行。
const url = 'https://the-one-api.dev/v2/book'let prom = fetch(url)prom // Promise {<pending>}// wait a bitprom // Promise {<fullfilled>: Response}, if no errors// orprom // Promise {<rejected>: Error message}, if any error
在上述代碼段中,針對fetch的調用返回了一個狀態(tài)為“待處理(pending)”的Promise。而當API返回響應時,它將會轉換為“已實現(fulfilled)”的狀態(tài)。
在Promises中,你可以執(zhí)行如下操作,來通過API的調用,將響應解析成為JSON。
const url = 'https://the-one-api.dev/v2/book'let prom = fetch(url)prom // Promise {<fullfilled>: Response}.then(res => res.json()).then(json => console.log(json)) // prints response, if no errors.catch(err => console.log(err)) // prints error message, if any error
2017年,JavaScript推出了兩個新的關鍵字async和await,來使得Promises的處理和使用變得更加容易和流暢。當然,它們并非Promises的替代,只是Promises概念之上的語法增強。
而且,并非讓所有的代碼里都出現在一系列的“then”函數,await旨在讓其更像同步的JavaScript。你可以使用帶有await的try...catch,來代替直接使用Promise的catch函數去處理錯誤。下面是具有同等效果的await代碼。
const url = 'https://the-one-api.dev/v2/book'let res = await fetch(url) // Promise {<fullfilled>: Response} -await-> Responsetry {let json = await res.json()console.log(json) // prints response, if no errors} catch(err) {console.log(err) // prints error message, if any error}
當然,async關鍵字也有著“硬幣的另一面”,它會將任何待發(fā)送的數據封裝到一個Promise中。下面是一段旨在通過異步函數添加多個數字的程序代碼。在現實情況中,您的代碼可能會比它更加復雜。
async function sum(...nums) {return nums.reduce((agg, val) => agg + val, 0)}sum(1, 2, 3) // Promise {<fulfilled>: 6}.then(res => console.log(res) // prints 6let res = await sum(1, 2, 3) // Promise {<fulfilled>: 6} -await-> 6console.log(res) // prints 6
小結
如您所見,JavaScript每年都會在其語言中加入新的功能。希望我們在上面介紹到的七項提到代碼質量的優(yōu)秀實踐,能夠對你的日常編程提供幫助。
學習更多技能
請點擊下方公眾號
![]()

