[譯] 優秀 JavaScript 開發人員應掌握的 9 個技巧

JintNiu發表於2019-01-22
[譯] 優秀 JavaScript 開發人員應掌握的 9 個技巧
Photo by Andrew Worley on Unsplash

原文連結:9 Tricks for Kickass JavaScript Developers in 2019
原文作者:Lukas Gisder-Dubé
譯者:JintNiu
推薦理由:JavaScript 已經成為了當今使用最為廣泛、最受歡迎的語言之一,掌握一些使用技巧不僅可以提高開發效率,更有利於思維轉換。


過去的一年, JavaScript 在持續變化著,其使用範圍也越來越廣。接下來,我將針對 JavaScript 的使用,列出 9 條 建議,以幫助你寫出更加整潔高效的程式碼,成為更好的開發者。

1. async/await

JavaScript 極速發展的今天,回撥地獄所產生的問題已不復存在。實際開發過程中我們應當儘量避免使用回撥函式,除非為了遵守程式碼庫規則或是維護效能。而解決回撥地獄的一個常用方法為 Promise,但在程式碼量較多時使用會適得其反。於是提出了 async / await,使程式碼結構更加清晰明瞭,便於閱讀和維護。一般而言,可以 await 任何 Promise 以防止正使用的庫的返回值為 Promise ,也就是說 async/awaitPromise 的語法糖,而且使用方法也十分簡單:在函式前加 async。下面是一個簡單的例子:

async function getData() { 
const result = await axios.get('https://dube.io/service/ping') const data = result.data console.log('data', data) return data
}getData()複製程式碼

await 只能使用在 async 函式中,不能用於全域性作用域。

async/await 是 ES2017 中引入的,使用時請進行轉換。

2. 非同步控制流

當我們進行非同步呼叫並獲得返回值時,通常期望直接獲取多個資料集,並且分別操作每個資料集。因此有了以下方式:

for…of

假設頁面上要展示 Pokemon 資料,可以通過 axios 獲取它們的詳細資訊,我們所期望的是在得到返回值時立即更新頁面中的所有資料,而不是等所有呼叫完成後才進行更新。

我們可以使用 for...of 解決上述問題。 首先迴圈遍歷陣列,並在每個迴圈內執行非同步程式碼,當所有呼叫都成功時跳出迴圈。需要注意的是,這種方法雖然會對效能產生一些影響,但也不乏是一個很好的方法。

以下是一個例子:

import axios from 'axios'let myData = [{ 
id: 0
}, {
id: 1
}, {
id: 2
}, {
id: 3
}]async function fetchData(dataSet) {
for (entry of dataSet) {
const result = await axios.get(`https://ironhack-pokeapi.herokuapp.com/pokemon/${entry.id
}
`
) const newData = result.data updateData(newData) console.log(myData)
}
}function updateData(newData) {
myData = myData.map(el =>
{
if (el.id === newData.id) return newData return el
})
}fetchData(myData)複製程式碼

可以將這些例子複製貼上到編輯器中除錯執行。

譯者注:除了迴圈本身帶來的效能問題之外,在使用 async/await 處理非同步請求時也會對效能造成影響:如果使用過多 await 語句,而且候這些語句並不需要依賴於之前的語句,則會產生 async/await 地獄,影響效能。

Promise.all

如果想要並行獲取所有的 Pokemon,我們可以使用 Promise.all 方法來 await 所有 Promise

import axios from 'axios'  let myData = [{ 
id: 0
}, {
id: 1
}, {
id: 2
}, {
id: 3
}]async function fetchData(dataSet) {
const pokemonPromises = dataSet.map(entry =>
{
return axios.get(`https://ironhack-pokeapi.herokuapp.com/pokemon/${entry.id
}
`
)
}) const results = await Promise.all(pokemonPromises) results.forEach(result =>
{
updateData(result.data)
}) console.log(myData)
}function updateData(newData) {
myData = myData.map(el =>
{
if (el.id === newData.id) return newData return el
})
}fetchData(myData) 複製程式碼

for...ofPromise.all 都是 ES6+ 引入的,使用時請進行轉換。

3. 解構賦值 &
預設值

回到上個例子:

const result = axios.get(`https://ironhack-pokeapi.herokuapp.com/pokemon/${entry.id
}
`
)const data = result.data複製程式碼

現在有一種更簡單的方法來實現它:通過解構賦值的方式從物件或陣列中獲取一個或多個值:

const { 
data
} = await axios.get(...)複製程式碼

也可對變數重新命名:

const { 
data: newData
} = await axios.get(...)複製程式碼

另一種方法是在解構賦值時指定預設值,這樣做可以確保程式碼不會出現 undefined,也避免手動檢查變數的麻煩。

const { 
id = 5
} = {
}console.log(id) // 5複製程式碼

這些方法也可以用於函式引數,例如:

function calculate({ 
operands = [1, 2], type = 'addition'
} = {
}
)
{
return operands.reduce((acc, val) =>
{
switch (type) {
case 'addition': return acc + val case 'subtraction': return acc - val case 'multiplication': return acc * val case 'division': return acc / val
}
}, ['addition', 'subtraction'].includes(type) ? 0 : 1)
}console.log(calculate()) // 3 console.log(calculate({
type: 'division'
})) // 0.5 console.log(calculate({
operands: [2, 3, 4], type: 'multiplication'
})) // 24 複製程式碼

ES6 引入瞭解構賦值和預設值,使用時請進行轉換。

4. 真值和虛值

當我們使用預設值時,通常要對現有值進行一系列判斷,這種方法使程式碼變得異常繁瑣,而現在我們可以真值(Truthy)和虛值(Falsy)的方式來改進它,不僅可以節省程式碼量,還使人更加信服。

以下是之前的做法:

if (myBool === true) { 
console.log(...)
}// ORif (myString.length >
0) {
console.log(...)
}// ORif (isNaN(myNumber)) {
console.log(...)
}複製程式碼

簡化後:

if (myBool) { 
console.log(...)
}// ORif (myString) {
console.log(...)
}// ORif (!myNumber) {
console.log(...)
}複製程式碼

以下為 FalsyTruthy 的概念:

Falsy

  • 長度為0的字串
  • 數字 0
  • false
  • undefined
  • null
  • NaN

Truthy

  • 空陣列
  • 空物件
  • 其他

使用真值和虛值時沒有確切的比較方式,這類似於我們進行比較時常使用雙等號 == 而不是三等號 ===。一般而言,這兩者的判定方式相同,但在某些情況下也會遇到一些錯誤,對我來說主要為數字 0

[譯] 優秀 JavaScript 開發人員應掌握的 9 個技巧
Photo by Philippe Leone on Unsplash

5. 邏輯運算子和三元運算子

邏輯運算子和三元運算子主要用於精簡程式碼,有助於保持程式碼整潔度,但當他們形成運算鏈時會顯得雜亂。

邏輯運算子

邏輯運算子:和(&
&
)、或(||),一般用於比較兩個表示式,返回值為: truefalse 或著它的匹配值。如下例:

console.log(true &
&
true) // trueconsole.log(false &
&
true) // falseconsole.log(true &
&
false) // falseconsole.log(false &
&
false) // falseconsole.log(true || true) // trueconsole.log(true || false) // trueconsole.log(false || true) // trueconsole.log(false || false) // false複製程式碼

我們可以將邏輯運算子與真值和虛值的相關知識結合起來。

如果有表示式 AB,針對兩種邏輯運算子,有以下規則:

  • A &
    &
    B
    : 當 Afalse 時則直接返回 A 的值 ;否則返回 B 的值。
  • A || B : 當 Atrue 時則直接返回 A 的值 ;否則返回 B 的值。

譯者注:上述規則為邏輯運算中的短路現象。

console.log(0 &
&
{
a: 1
}) // 0console.log(false &
&
'a') // falseconsole.log('2' &
&
5) // 5console.log([] || false) // []console.log(NaN || null) // nullconsole.log(true || 'a') // true複製程式碼

三元運算子

三元運算子與邏輯運算子非常相似,但有由三個部分組成:

  1. 條件表示式:其結果為真值或是虛值
  2. 返回值 1:條件表示式為真值時,返回該值
  3. 返回值 2:條件表示式為虛值時,返回該值

例如:

const lang = 'German'console.log(lang === 'German' ? 'Hallo' : 'Hello') // Halloconsole.log(lang ? 'Ja' : 'Yes') // Jaconsole.log(lang === 'French' ? 'Bon soir' : 'Good evening') // Good eveing複製程式碼

6. 自判斷連結

當訪問某個巢狀物件的屬性時,由於不能確定目標物件或者屬性性是否存在,而需要進行一系列判斷:

let dataif (myObj &
&
myObj.firstProp &
&
myObj.firstProp.secondProp &
&
myObj.firstProp.secondProp.actualData) data = myObj.firstProp.secondProp.actualData複製程式碼

顯而易見,程式碼變得非常臃腫難看。而自判斷連結(optional chaining)的提出,正好可以滿足對巢狀屬性的校驗需求,並使程式碼更加清晰整潔。如下例:

const data = myObj?.firstProp?.secondProp?.actualData複製程式碼

譯者注:自判斷連結: 檢查一個物件上面是否存在某屬性。
出現原因:呼叫某 Object屬性鏈中的某個屬性時,如果該屬性不存在,會導致 Cannot read property xxx of undefined 錯誤。於是自判斷連結 ?. 出現。
使用方式:obj?.a?.b?.c。依次對程式碼中的屬性進行判斷,如果為 null 或者 undefined 時,結束呼叫,返回 undefined

目前,自判斷連結還未納入官方規範中,只處於第一階段的實驗特性。您需要在 babelrc 中新增 @ babel / plugin-proposal-optional-chaining 後方可使用它。

7. 類屬性 &
繫結

JavaScript 中經常會用到繫結(bind)。ES6 規範中箭頭函式的引入,使 JavaScript 開發人員有了一種將函式自動繫結到執行上下文中的常用方法,同時這種方法非常重要。

由於 JavaScript 中的類方法有特定的呼叫方式,因此當我們首次宣告一個類時不能使用箭頭函式,因此需要在其他位置進行函式繫結,比如在建構函式中(以 React.js 為例)。工作當中我總是先定義類方法再對其進行繫結,這種方法非常繁瑣且容易出錯。但如果使用 class 語法,我們可以通過箭頭函式自動繫結它。以下是繫結 _increaseCount 的例子:

 class Counter extends React.Component { 
constructor(props) {
super(props) this.state = {
count: 0
}
} render() {
return ( <
div>
<
h1>
{this.state.count
}<
/h1>
<
button onClick={this._increaseCount
}
>
Increase Count<
/button>
<
/div>
)
} _increaseCount = () =>
{
this.setState({
count: this.state.count + 1
})
}
} 複製程式碼

目前,類屬性還未納入官方規範中,只處於第三階段的實驗特性。您需要在 babelrc 中新增 @ babel / plugin-proposal-class-properties 後方可使用。

8. 使用 ParcelJS

作為前端開發人員,保證會有打包專案或著轉換程式碼的需求,對此,webpack 已經在很久之前提出先關規範了。第一次使用 webpack v1.0 時,我花了很長時間進行配置,雖然最終執行成功,但整個過程非常痛苦,而且成功後的我變得畏手畏腳,生怕破壞之前的配置。直到幾個月前,ParcelJS 的發現使我心情大好,在提供開箱即用功能的同時,它還實現了按需配置,也可以支援類似於 webpack 或 babel 的外掛系統,最重要的是它的速度極快。

譯者注:ParcelJS 官網顯示,parcelJS 的打包速度比 webpack 快 2 倍以上。

9. 封裝自己的元件庫

這是一個非常有趣的話題,關於它我有很多的想法。對於 CSS,很多人更傾向於使用類似於 BootStrap 這樣的元件庫。而對於 JavaScript,仍然有人呼叫 jQuery 或者其他庫來實現驗證、滑塊等功能。首先不否認使用各種庫的好處,但還是強烈建議可以親手實現這些功能,而不是盲目地安裝 npm 包。當整個團隊正構建一個類似於 moment.jsreact-datepicker 的大型庫(甚至框架)時,你沒必要親手實現它,但可以封裝為屬於自己的元件庫,而且在實現元件庫的同時,您可以:

  1. 準確掌握程式碼的結構以及執行機制
  2. 真正理解程式設計及其工作原理
  3. 防止程式碼庫變得臃腫

直接使用 npm 包是當然非常容易,但如果想要實現某些 npm 包中不具備的功能時則會需要更多的時間:如果軟體沒有按預期正常工作,或者要將其轉換為另一個軟體包,您將會花費更多時間來了解其 API 的配置方式。因此,您可以為自己量身定做一套資料自己的元件庫。


關於作者:Lukas Gisder-Dubé 元件並領導了一家初創公司,期間建立了自己的技術團隊,並任職 CTO 一年半。 離開創業公司後,在 Ironhack 擔任首席講師。如今在柏林正建立一家創業諮詢公司。檢視 dube.io 以瞭解更多資訊。

[譯] 優秀 JavaScript 開發人員應掌握的 9 個技巧

翻譯參考

  1. 怎樣處理 async/await 浪費效能問題
  2. Async/Await 優於 Promise 的 6 個理由
  3. MDN – Falsy
  4. MDN – Truthy
  5. Optional Chaining for JavaScript

來源:https://juejin.im/post/5c4506c9e51d45524c7cf206#comment

相關文章