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

Photo by Andrew Worley on Unsplash

原文連接:9 Tricks for Kickass JavaScript Developers in 2019
原文做者:Lukas Gisder-Dubé
譯者:JintNiu
推薦理由:JavaScript 已經成爲了當今使用最爲普遍、最受歡迎的語言之一,掌握一些使用技巧不只能夠提升開發效率,更有利於思惟轉換。javascript


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

1. async/await

JavaScript 極速發展的今天,回調地獄所產生的問題已不復存在。實際開發過程當中咱們應當儘可能避免使用回調函數,除非爲了遵照代碼庫規則或是維護性能。而解決回調地獄的一個經常使用方法爲 Promise,但在代碼量較多時使用會拔苗助長。因而提出了 async / await,使代碼結構更加清晰明瞭,便於閱讀和維護。通常而言,能夠 await 任何 Promise 以防止正使用的庫的返回值爲 Promise ,也就是說 async/awaitPromise 的語法糖,並且使用方法也十分簡單:在函數前加 async。下面是一個簡單的例子:html

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 中引入的,使用時請進行轉換。java

2. 異步控制流

當咱們進行異步調用並得到返回值時,一般指望直接獲取多個數據集,而且分別操做每一個數據集。所以有了如下方式:react

for...of

假設頁面上要展現 Pokemon 數據,能夠經過 axios 獲取它們的詳細信息,咱們所指望的是在獲得返回值時當即更新頁面中的全部數據,而不是等全部調用完成後才進行更新。webpack

咱們可使用 for...of 解決上述問題。 首先循環遍歷數組,並在每一個循環內執行異步代碼,當全部調用都成功時跳出循環。須要注意的是,這種方法雖然會對性能產生一些影響,但也不乏是一個很好的方法。ios

如下是一個例子:git

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)
複製代碼

能夠將這些例子複製粘貼到編輯器中調試運行。github

譯者注:除了循環自己帶來的性能問題以外,在使用 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(...)
}
// 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(...)
}
複製代碼

如下爲 FalsyTruthy 的概念:

Falsy

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

Truthy

  • 空數組
  • 空對象
  • 其餘

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

Photo by Philippe Leone on Unsplash

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

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

邏輯運算符

邏輯運算符:和(&&)、或(||),通常用於比較兩個表達式,返回值爲: truefalse 或着它的匹配值。以下例:

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
複製代碼

咱們能夠將邏輯運算符與真值和虛值的相關知識結合起來。

若是有表達式 AB,針對兩種邏輯運算符,有如下規則:

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

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

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
複製代碼

三元運算符

三元運算符與邏輯運算符很是類似,但有由三個部分組成:

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

例如:

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 eveing
複製代碼

6. 自判斷連接

當訪問某個嵌套對象的屬性時,因爲不能肯定目標對象或者屬性性是否存在,而須要進行一系列判斷:

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
複製代碼

譯者注:自判斷連接: 檢查一個對象上面是否存在某屬性。
出現緣由:調用某 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 以瞭解更多信息。


翻譯參考

  1. 怎樣處理 async/await 浪費性能問題
  2. Async/Await 優於 Promise 的 6 個理由
  3. MDN - Falsy
  4. MDN - Truthy
  5. Optional Chaining for JavaScript
相關文章
相關標籤/搜索