JavaScript7种提高代码质量最佳方法


如果您今天编写JavaScript,那么值得您花时间了解该语言在过去几年中看到的所有更新。自2015年以来,随着ES6的发布,每年都会发布新版本的ECMAScript规范。每次迭代都为该语言添加了新功能,新语法和生活质量改进。大多数浏览器和Node.js中的JavaScript引擎很快就会赶上,并且您的代码也应该赶上也很公平。这是因为随着JavaScript的每次新迭代,都会出现新的习惯用法和新的方式来表达您的代码,而且很多时候,这些更改可能会使您和您的协作者更易于维护代码。

这里是一些最新的ECMAScript功能,通过归纳,JavaScript和Node.js,您可以利用它们编写更简洁,更简洁和更易读的代码。

1.整块计分声明 自从该语言问世以来,JavaScript开发人员一直var在声明变量。关键字var具有其怪癖,其中最成问题的是使用它创建的变量的范围。

var x = 10
if (true) {
  var x = 15     // inner declaration overrides declaration in parent scope
  console.log(x) // prints 15
}
console.log(x)   // prints 15

由于使用定义的变量var不是块作用域,因此在较窄的范围内重新定义它们会影响外部范围的值。

现在,我们有两个新的关键字replace var,即let,const并且没有这个缺点。

let y = 10
if (true) {
  let y = 15       // inner declaration is scoped within the if block
  console.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 Arrow Functions是最近引入JavaScript的另一个非常重要的功能。它们具有许多优点。首先,它们使JavaScript的功能方面看起来更漂亮,并且编写起来更简单。

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

在以上所有示例中,以独特箭头命名的箭头=>函数用简洁的语法替换了传统函数。

如果函数主体是单个表达式,则隐含范围括号{}和return关键字,无需编写。 如果函数具有单个参数,则参数括号()是隐含的,无需编写。 如果函数体表达式是字典,则必须将其括在括号中()。 箭头函数的另一个重要优点是它们不定义范围,而是存在于父范围内。这样可以避免由于使用this关键字而引起的许多陷阱。箭头函数没有的绑定this。在arrow函数中,的值this与父作用域中的值相同。因此,箭头函数不能用作方法或构造函数。箭头函数不适用于apply,bind或call,也没有的绑定super。

它们还具有某些其他限制,例如缺少arguments传统功能可以访问的对象以及无法yield从功能主体访问。

因此,箭头功能不是标准功能的1:1替代,而是欢迎添加到JavaScript的功能集中。

3.可选链接 person在这里想象一个深嵌套的数据结构,像这个对象。考虑您要访问此人的名字和姓氏。您可以这样用JavaScript编写此代码:

person = {
  name: {
    first: 'John',
    last: 'Doe',
  },
  age: 42
}
person.name.first // 'John'
person.name.last  // 'Doe'

现在想象一下,如果person对象不包含嵌套name对象,将会发生什么。

person = {
  age: 42
}
person.name.first // TypeError: Cannot read property 'first' of undefined
person.name.last  // TypeError: Cannot read property 'last' of undefined

为避免此类错误,开发人员不得不诉诸如下代码,这些代码不必要地冗长,难以阅读且难以编写,这是一个非常糟糕的形容词三重奏。

person && person.name && person.name.first // undefined

满足可选链接的要求,这是JavaScript的一项新功能,可以消除这种怪异现象。可选的链接会在挖掘过程遇到anull或undefinedvalue并返回undefined而不会引起错误的情况下将其短路。

person?.name?.first // undefined

结果代码简洁明了。

4.空合并 在引入空值合并运算符之前,JavaScript开发人员使用OR运算符||(如果缺少输入)回退到默认值。这带有重大警告,即使合法但虚假的值也将导致默认值的回退。

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的情况下,它才会导致回退。此处的空值是指null或的值undefined。

function print(val) {
    return val ?? 'Missing'
}

print(undefined) // 'Missing'
print(null)      // 'Missing'

print(0)         // 0
print('')        // ''
print(false)     // false
print(NaN)       // NaN

这样,您可以确保如果程序接受伪造的值作为合法输入,则不会最终将其替换为后备。

5.逻辑分配 假设您要在且仅当该值当前为空时才为该变量分配一个值。逻辑上这样写是这样的:

if (x === null || x == undefined) {
    x = y
}

如果您知道短路的工作原理,则可能希望使用零位合并运算符将这三行代码替换为更简洁的版本。

x ?? (x = y) // x = y if x is nullish, else no effect

在这里,我们使用null ish合并运算符的短路功能来执行第二部分(x = y如果x为null ish的话)。该代码非常简洁,但仍不太容易阅读或理解。逻辑为零的分配消除了这种变通方法的需要。

x ??= y // x = y if x is nullish, else no effect

同样,JavaScript还引入了逻辑AND赋值&&=和逻辑OR赋值||=运算符。这些操作员仅在满足特定条件时执行分配,否则不起作用。

x ||= y // x = y if x is falsy, else no effect
x &&= y // x = y if x is truthy, else no effect

专家提示:如果您以前已经编写过Ruby,那么您会看到||=and&&=运算符,因为Ruby没有虚假值的概念。

6.命名捕获组 让我们开始快速回顾一下正则表达式中的捕获组。捕获组是与括号中的正则表达式部分匹配的字符串的一部分。

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 match
result[1] // '2020', the first capture group
result[2] // '03', the second capture group
result[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.groups

year  // '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 bit
prom // Promise {<fullfilled>: Response}, if no errors
// or
prom // Promise {<rejected>: Error message}, if any error

在这里,对fetch的调用返回一个Promise,该Promise在创建时的状态为“待处理”。很快,当API返回响应时,它将转换为“已实现”状态,并且可以访问包装的响应。在Promises世界中,您将执行以下操作来进行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。另外一个好处是,您可以使用try...catchwithawait而不是像直接使用Promises那样处理'catch'函数中的错误。相同的代码await如下所示。

const url = 'https://the-one-api.dev/v2/book'
let res = await fetch(url) // Promise {<fullfilled>: Response} -await-> Response
try {
    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关键字是相同的硬币的另一侧,在它包装的任何数据到一个无极内被发送。考虑以下异步函数以添加多个数字。在现实世界中,您的代码将执行更为复杂的操作。

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 6

let res = await sum(1, 2, 3)    // Promise {<fulfilled>: 6} -await-> 6
console.log(res)                // prints 6

结论 这些新功能只是冰山一角。我们几乎没有刮过表面。JavaScript不断发展,并且每年都会在语言中添加新功能。很难跟上手动引入该语言的新功能和惯用语言的不断涌入。

如果某些工具可以为我们解决这个问题,那不是很好吗?不用,那里。我们已经详细讨论了使用ESLint在JavaScript存储库中设置静态代码分析的问题。它非常有用,应该是您工具链中必不可少的工具。但老实说,设置ESLint自动修复管道和流程需要花费时间和精力。除非您喜欢这种管道,否则如果您编写代码并将管道外包给DeepSource会更好。

DeepSource可以帮助您自动执行代码审查,并节省大量时间。只需.deepsource.toml在存储库的根目录中添加一个文件,DeepSource便会立即选择要扫描的文件。扫描将发现整个代码的改进范围,并通过有用的说明帮助您修复它们。


原文链接:https://codingdict.com/