【譯】12個編寫乾淨且可擴充套件的JavaScript技巧

米淇淋發表於2019-06-04

JavaScript起源於早期的網路。 從作為指令碼語言開始,到現在它已經發展成為一種完全成熟的程式語言,並且支援伺服器端執行。

現代Web應用程式嚴重依賴JavaScript,尤其是單頁應用程式(SPA)。藉助於React,AngularJS和Vue.js等新興框架,Web應用程式主要使用JavaScript構建。

擴充套件這些應用程式有時候會比較棘手,通過簡單的設定,您最終可能會遇到限制並迷失在混亂的海洋中。我想分享一些小技巧,這些技巧將幫助您以有效的方式編寫乾淨的程式碼。

本文面向任何技能水平的JavaScript開發人員。 但是,至少具有JavaScript中級知識的開發人員將從這些技巧中獲益最多。

原文連結:blog.logrocket.com/12-tips-for…

分隔您的程式碼

我建議保持程式碼庫清潔和可讀的最重要的事情是具有按主題分隔的特定邏輯塊(通常是函式)。如果你編寫一個函式,該函式應該預設只有一個目的,不應該一次做多個事情。

此外,您應避免引起副作用,這意味著在大多數情況下,您不應更改在函式外宣告的任何內容。 您將資料接收到帶引數的函式中;其他一切都不應該被訪問。如果您希望從函式中獲取某些內容,請返回新值。

模組化

當然,如果以類似的方式使用這些函式或執行類似的操作,您可以將多個函式分組到一個模組(and/or 的類中)。例如,如果要進行許多不同的計算,請將它們拆分為可以連結的獨立步驟(函式)。但是,這些函式都可以在一個檔案(模組)中宣告。 以下是JavaScript中的示例:

function add(a, b) {
    return a + b   
}

function subtract(a, b) {
    return a - b   
}

module.exports = {
    add,
    subtract
}

const { add, subtract } = require('./calculations')

console.log(subtract(5, add(3, 2))
複製程式碼

如果您正在編寫前端JavaScript,請務必使用預設匯出作為最重要的專案,併為次要專案命名匯出。

多個引數優先於單個物件引數

宣告一個函式時,您應該總是喜歡多個引數而不是一個期望物件的引數:

// GOOD
function displayUser(firstName, lastName, age) {
    console.log(`This is ${firstName} ${lastName}. She is ${age} years old.`)
}

// BAD
function displayUser(user) {
    console.log(`This is ${user.firstName} ${user.lastName}. She is ${user.age} years old.`)
}
複製程式碼

這背後的原因是,當您檢視函式宣告的第一行時,您能確切知道需要傳遞給函式的內容。

儘管函式應該受到限制 - 只做一項工作 - 但是它可能會變得更大。在函式體中掃描需要傳遞的變數(巢狀在物件中)將花費更多時間。有時,使用整個物件並將其傳遞給函式似乎更容易,但為了擴充套件應用程式,此設定肯定會有所幫助。

在某種程度上,宣告特定引數沒有意義。對我來說,它超過四個或五個功能引數。如果你的函式變大,你應該轉向使用物件引數。

這裡的主要原因是引數需要以特定順序傳遞。 如果您有可選引數,則需要傳遞undefined或null。 使用物件引數,您可以簡單地傳遞整個物件,其中順序和未定義的值無關緊要。

解構(Destructuring)

解構是ES6引入的一個很好的工具。它允許您從物件中獲取特定欄位並立即將其分配給變數。 您可以將它用於任何型別的物件或模組。

// EXAMPLE FOR MODULES
const { add, subtract } = require('./calculations')
複製程式碼

只匯入您需要在檔案中使用的函式而不是整個模組,然後從中訪問特定的函式。 同樣,當您確定您確實需要一個物件作為函式引數時,也可以使用destructuring。 這仍將為您提供函式內所需內容的概述:

function logCountry({name, code, language, currency, population, continent}) {
    let msg = `The official language of ${name} `
    if(code) msg += `(${code}) `
    msg += `is ${language}. ${population} inhabitants pay in ${currency}.`
    if(contintent) msg += ` The country is located in ${continent}`
}

logCountry({
    name: 'Germany',
    code: 'DE',
    language 'german',
    currency: 'Euro',
    population: '82 Million',
})

logCountry({
    name: 'China',
    language 'mandarin',
    currency: 'Renminbi',
    population: '1.4 Billion',
    continent: 'Asia',
})
複製程式碼

正如你所看到的,我仍然知道我需要傳遞什麼給函式 - 即使它被包裝在一個物件中。要解決了解所需內容的問題,請參閱下一個提示!(順便說一句,這也適用於React功能元件。)

使用預設值

解構的預設值甚至基本函式引數都非常有用。首先,它們為您提供了一個可以傳遞給函式的值的示例。其次,您可以指出哪些值是必需的,哪些值不是。使用前面的示例,該函式的完整設定如下所示:

function logCountry({
    name = 'United States', 
    code, 
    language = 'English', 
    currency = 'USD', 
    population = '327 Million', 
    continent,
}) {
    let msg = `The official language of ${name} `
    if(code) msg += `(${code}) `
    msg += `is ${language}. ${population} inhabitants pay in ${currency}.`
    if(contintent) msg += ` The country is located in ${continent}`
}

logCountry({
    name: 'Germany',
    code: 'DE',
    language 'german',
    currency: 'Euro',
    population: '82 Million',
})

logCountry({
    name: 'China',
    language 'mandarin',
    currency: 'Renminbi',
    population: '1.4 Billion',
    continent: 'Asia',
})
複製程式碼

顯然,有時您可能不想使用預設值,而是在未傳遞值時丟擲錯誤。 然而,這通常是一個方便的技巧。

資料稀缺性

前面的技巧引出了一個結論:最好不要傳遞您不需要的資料。同樣,在設定函式時,這可能意味著更多的工作。但是,從長遠來看,它肯定會為您提供更具可讀性的程式碼庫。確切地知道在特定位置使用哪些值是非常有價值的。

行數和縮排限制

我見過大檔案 - 非常大的檔案。實際上,超過3,000行程式碼。在這些檔案中查詢邏輯塊是非常困難的。

因此,您應該將檔案大小限制為一定數量的行。我傾向於將我的檔案儲存在100行程式碼之下。 有時候,很難分解檔案,它們會增長到200-300行,在極少數情況下會增加到400行。

超過此臨界值,意味著檔案太雜亂,難以維護。隨意建立新的模組和資料夾。您的專案應該看起來像一個森林,由樹(模組部分)和分支(模組和模組檔案組)組成。避免試圖模仿阿爾卑斯山,在密閉區域堆積程式碼。

相比之下,你的實際檔案應該看起來像Shire,這裡和那裡都有一些山丘(小水平的縮排),但一切都相對平坦。 儘量將壓痕水平保持在四級以下。

也許為這些提示啟用eslint規則是有幫助的!

使用prettier

在團隊中工作需要清晰的樣式指南和格式。ESLint提供了一個巨大的規則集,您可以根據自己的需求進行自定義。還有 eslint--fix,它可以糾正一些錯誤,但不是全部。

相反,我建議使用Prettier格式化程式碼。這樣,開發人員不必擔心程式碼格式化,而只需編寫高質量的程式碼。 外觀將一致並且格式自動化。

使用有意義的變數名

理想情況下,應根據其內容命名變數。 以下是一些有助於您宣告有意義的變數名稱的指南。

函式

函式通常執行某種操作。為了解釋這一點,人類使用動詞 - 轉換或顯示,例如。在開頭用動詞命名函式是個好主意,例如convertCurrencydisplayUserName

陣列

這些通常會包含一系列專案; 因此,將s附加到變數名稱。 例如:

const students = ['Eddie', 'Julia', 'Nathan', 'Theresa']
複製程式碼

布林

簡單地說就是儘量多接近於自然語言,這樣好理解。你會問“這個人是教師嗎?”→“是”或“否”。同樣:

const isTeacher = true // OR false
複製程式碼

陣列函式

forEach, map, reduce, filter等是很好的原生JavaScript函式,用於處理陣列和執行某些操作。 我看到很多人只是將elelement作為引數傳遞給回撥函式。 雖然這很簡單快捷,但您還應根據其值來命名。 例如:

const cities = ['Berlin', 'San Francisco', 'Tel Aviv', 'Seoul']
cities.forEach(function(city) {
...
})
複製程式碼

標識

通常,您必須跟蹤特定資料集和物件的ID。當巢狀id時,只需將其保留為id即可。在這裡,我喜歡在將物件返回到前端之前將MongoDB _id對映到 id。從物件中提取id時,請預先新增物件的型別。例如:

const studentId = student.id
// OR
const { id: studentId } = student // destructuring with renaming
複製程式碼

該規則的一個例外是模型中的MongoDB引用。 在這裡,只需在引用的模型之後命名欄位即可。 這將在填充參考文件時保持清晰:

const StudentSchema = new Schema({
    teacher: {
        type: Schema.Types.ObjectId,
        ref: 'Teacher',
        required: true,
    },
    name: String,
    ...
})
複製程式碼

儘可能使用async / await

在可讀性方面,回撥是最糟糕的 - 特別是在巢狀時。Promises是一個很好的改進,但在我看來,async / await具有最好的可讀性。即使對於初學者或來自其他語言的人來說,這也會有很大幫助。但是,請確保您瞭解其背後的概念,並且不要盲目地在任何地方使用它。

模組匯入順序

正如我們在技巧1和2中看到的那樣,將邏輯保持在正確的位置是可維護性的關鍵。 同樣,匯入不同模組的方式可以減少檔案中的混淆。 我在匯入不同模組時遵循一個簡單的結構:

// 3rd party packages
import React from 'react'
import styled from 'styled-components'

// Stores
import Store from '~/Store

// reusable components
import Button from '~/components/Button'

// utility functions
import { add, subtract } from '~/utils/calculate'

// submodules
import Intro from './Intro'
import Selector from './Selector'
複製程式碼

我在這裡使用了React元件作為示例,因為有更多型別的匯入。 您應該能夠根據您的具體用例進行調整。

擺脫控制檯

console.log 是一種很好的除錯方式 - 非常簡單,快速,完成工作。顯然,有更復雜的工具,但我認為每個開發人員仍然使用它。如果您忘記清理日誌,您的控制檯最終將陷入巨大的混亂。然後,您確實要在程式碼庫中保留日誌; 例如,警告和錯誤。

要解決此問題,您仍然可以使用console.log 進行除錯,但對於持久日誌,請使用loglevelwinston等庫。此外,您可以使用ESLint警告控制檯語句。這樣你就可以輕鬆地全域性查詢console... 並刪除這些語句。

遵循這些指導原則確實幫助我保持程式碼庫的清潔和可擴充套件性。 你覺得還有什麼提示特別有用的嗎?請在評論中告訴我您在編碼工作過程中值得推薦的內容,並請分享您用於幫助程式碼結構的任何其他提示!謝謝~

如果覺得文章對你有些許幫助,歡迎在我的GitHub部落格點贊和關注,感激不盡!

相關文章