- 原文地址:levelup.gitconnected.com/9-tricks-fo…
- 原文做者:Lukas Gisder-Dubé
- Markdown 地址:github.com/wanghaiqion…
又一年過去了,JavaScript 也一直在改變。不過有些技巧能夠幫助你寫出簡潔高效可伸縮的代碼,即使是(或者說特別是)2019 年。下面 9 條實用小技巧能助你成爲一個更好的開發者。javascript
若是你仍深陷回調地獄,那麼你應該還在寫 2014 年以前的老古董代碼吧。除非頗有必要,好比遵照代碼庫要求或者出於性能緣由,不然不要使用回調方式。Promise 還行,但若是你的代碼日漸龐大,Promise 就顯得有些尷尬了。我如今的首選方案是 async / await
,它讓代碼的閱讀與改進都變得簡潔不少。事實上,你能夠 await
JavaScript 中的每個 Promise,若是你用的庫函數返回一個 Promise,就能夠簡單地 await
之。其實,async / await
只是使用 Promise 的語法糖。想讓你的代碼正常工做起來,你只須要在 funcion 前增長 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
函數中使用。java
async / await
是在 ES2017 中引入的,因此記得轉譯你的代碼。react
實際開發中不可避免地常常會遇到這種狀況,咱們要獲取多個數據項而後分別對它們進行某些處理(for…of
),或者須要在全部異步調用都獲得返回值後再完成某項任務(Promise.all)。webpack
比方說咱們要獲取頁面中幾個 Pokemon 的具體信息,咱們並不想等待全部調用所有完成,尤爲是有時候並不知道具體有多少次調用,但咱們想只要一有返回數據就當即更新數據項。這時候咱們就能夠用 for...of
來遍歷數組,在循環體內執行 async 代碼,代碼的執行會被暫停,直到每次調用成功。必須注意的是若是你在代碼中如示例這樣作,可能會帶來性能瓶頸,但把這個技巧收藏你的工具箱裏仍是很是有用的。示例以下:ios
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)
複製代碼
注:這些示例均可有效運行,可隨意複製粘貼到你喜歡的代碼沙盒內執行(如 jsfiddle、jsbin、codepen)。git
若是想並行獲取全部 Pokemon 的信息又該如何實現呢?既然 await
能夠用在全部 Promise 上,很簡單,用 Promise.all
:github
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...of
與 Promise.all
都是在 ES6+ 引入的,因此(必要的話)記得轉譯你的代碼。web
讓咱們返回到上一示例中,咱們是這樣作的:npm
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 中被引入,確保先轉譯你的代碼。
在肯定是否要取默認值的時候,咱們每每會先對給定的值進行檢查,其中的某些檢查如今來講已經沒有必要了,將成爲歷史。不管如何,知道如何處理 真值
(truthy values)和 假值
(falsy values)老是很是好的。它能幫助咱們改進代碼,省去一些表達式,讓代碼更清晰明白。我常常看到有人這樣作:
if(myBool === true) {
console.log(...)
}
// OR
if(myString.length > 0) {
console.log(...)
}
// OR
if(isNaN(myNumber)) {
console.log(...)
}
複製代碼
這些均可以簡寫成:
if(myBool) {
console.log(...)
}
// OR
if(myString) {
console.log(...)
}
// OR
if(!myNumber) {
console.log(...)
}
複製代碼
想要真正用好這些,你須要理解 真值
和 假值
的含義。這裏有個小總結:
假值
0
false
undefined
null
NaN
真值
注意在檢測真假值時,這裏進行的是非嚴格比較,也就是說用的是 ==
而不是 ===
。通常說來,兩者行爲相同,但在某些特定狀況下會出現 bug。對我來講,常發生在數字 0
上。
一樣,這也是精簡代碼的好方法。一般都能幫咱們簡化代碼,但也會帶來一些混亂,尤爲是鏈式使用時。
邏輯運算符主要用於鏈接兩個表達式,計算返回 true
,false
或者與之匹配的值,&&
表示邏輯與,||
表示邏輯或。以下:
console.log(true && true) // true
console.log(false && true) // false
console.log(true && false) // false
console.log(false && false) // false
console.log(true || true) // true
console.log(true || false) // true
console.log(false || true) // true
console.log(false || false) // false
複製代碼
咱們把邏輯運算符與上一個知識點真假值結合起來理解。當使用邏輯運算符時,聽從以下規則:
&&
:返回第一個假值,若是沒有,則返回最後一個真值||
:返回第一個真值,若是沒有,則返回最後一個假值console.log(0 && {a: 1}) // 0
console.log(false && 'a') // false
console.log('2' && 5) // 5
console.log([] || false) // []
console.log(NaN || null) // null
console.log(true || 'a') // true
複製代碼
三元運算符與邏輯運算符相似,但有三個部分:
示例以下:
const lang = 'German'
console.log(lang === 'German' ? 'Hallo' : 'Hello') // Hallo
console.log(lang ? 'Ja' : 'Yes') // Ja
console.log(lang === 'French' ? 'Bon soir' : 'Good evening') // Good evening
複製代碼
你是否遇到過這種問題,想要訪問嵌套對象的屬性,然而並不知道該對象或其中一個子屬性是否存在?你極可能會寫出相似這樣的代碼:
let data
if(myObj && myObj.firstProp && myObj.firstProp.secondProp && myObj.firstProp.secondProp.actualData)
data = myObj.firstProp.secondProp.actualData
複製代碼
這太囉嗦了,有個更好的方法,至少是一種更好的提議(繼續往下看如何用它)。它就是可選鏈式調用(optional chaining) ,用法以下:
const data = myObj?.firstProp?.secondProp?.actualData
複製代碼
我認爲,這是一種讓代碼更清晰的檢查嵌套屬性的有效方法。
注:目前可選鏈式調用 (optional chaining) 還不是官方規範的一部分,是處於 stage-1 的實驗性特性。你須要在你的 balelrc 中添加插件 @babel/plugin-proposal-optional-chaining 來使用。
函數綁定在 JavaScript 中十分常見。隨着 ES6 規範中箭頭函數的引入,咱們如今有辦法自動綁定函數到定義時的上下文了,這種方法很是好用,被 JavaScript 開發者普遍使用。Class(類)剛剛引入的時候,你並不能真正的使用箭頭函數,由於類方法須要一種特定的聲明方式。咱們要在其餘地方綁定函數,如在構造器中(React.js 的最佳實踐)。我一直以爲先定義類方法而後再綁定的流程很煩人,一段時候事後再看更感受莫名其妙。有了類屬性語法,咱們又能夠用箭頭函數得到自動綁定的好處。箭頭函數如今能夠在類內使用了。示例以下,重點看 _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 })
}
}
複製代碼
注:目前,class properties 並非正式官方規範的一部分,是處於 stage-3 的一個實驗性特性。須要在你的 balelrc 中添加插件 @babel/plugin-proposal-class-properties 來使用。
作爲前端開發者,你確定遇到過打包和轉譯代碼的狀況。wepback 成爲事實標準已經有很長一段時間了。我最初使用 webpack 時它還處於第一個版本,那時候很痛苦。我花了無數個小時去處理各類不一樣的配置項,讓項目打包運行。一旦能跑起來,我就不再會去動它們,怕又給弄壞了。幾個月前偶然發現的 parcel,讓我鬆了口氣。它提供的全部功能開箱即用,同時還容許咱們在必要時作出更改。它像 webpack 或者 babel 同樣支持插件系統,而且速度極快。若是你還沒聽過 parcel,牆裂建議去看看!
這是個很好的話題。關於這個問題,我有過不少不一樣的討論。即便是 CSS,有不少人也會傾向於使用組件庫,好比 bootstrap。JavaScript 的話,也有很多人使用 jQuery 和一些輕量代碼庫處理驗證、滑動效果等。雖然用庫也能夠理解,但我仍是牆裂建議本身編寫更多的代碼,而不是盲目地安裝 npm 包。對於那些整個團隊維護構建的大型代碼庫(或者框架),如 moment.js、react-datepicker,咱們我的嘗試去編寫是沒有什麼意義的。但能夠多寫一些只是本身項目使用的代碼。這樣對本身有三大好處:
一開始,用 npm 包會顯得更簡單,本身去實現某些功能反而更費時間。但萬一這個包並無像預期的那樣工做,而後你不得不換另外一個,花更多的時間去閱讀如何使用新的 API。若是是本身實現,你能夠按本身的使用狀況 100% 量身定製。