注:转载于 微信公众号的『前端新世界』原创翻译:
→ 被第3、4、5条 实践惊艳到了:推荐!!!
本文翻译自:
https://dev.to/i5han3/git-commit-message-convention-that-you-can-follow-1709
自2015年以来,随着ES6的发布,每年都会发布ECMAScript规范的新版本。每次迭代都为该语言添加了新的功能、新的语法和高质量的改进。接着大多数浏览器和Node.js中的JavaScript引擎也会很快追赶上来,所以我们的代码也应该与时俱进才可以——因为伴随着JavaScript的每一次新迭代,都会出现表达代码的新习惯和新方式,而且很多时候,这些更改可能会使得更易于我们维护代码。
下面有一些最新的ECMAScript功能,可以用来帮助我们编写更简洁,更明确和更易读的代码。
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
定义的变量不是块范围的,因此在较小的作用域内重新定义它们会影响外部作用域的值。
现在,我们有了两个新的关键字来替换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. 箭头函数
箭头函数是近期引入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
关键字。 - 如果函数只有单个参数,则不需要写参数括号
()
。 - 如果函数主体表达式是字典(
dictionary
),那必须将它封闭在括号()
内。
箭头函数的另一个重要优点是它们不定义范围,而是存在于父范围内。这避免了使用this
关键字可能会引起的许多陷阱。箭头函数对this
没有绑定。在箭头函数内部,this
的值与在父范围中的相同。因此,箭头函数不能用作方法或构造函数。箭头函数不适用于apply
,bind
或call
,也没有对super的绑定。
它们还有其他的一些局限性,例如缺少传统函数可以访问的参数对象以及无法从函数主体yield
。
因此,箭头函数不能百分百替代标准函数,但是完全可以添加到JavaScript的功能集中。
3. 可选链
假设有一个深层嵌套的数据结构,例如此处的person
对象。想好你要访问的那个人的名和姓,你可以像这样编写代码:
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的一项新功能,可以避免我们陷入这种尴尬的境地。可选链只要遇到null
或undefined
的值,就可以在未引发错误的情况下返回未undefined
。
person?.name?.first // undefined
代码简洁明了。
4. Nullish合并
在引入Nullish
合并运作符之前,JavaScript开发人员使用OR运算符||
(如果缺少输入)回退到默认值。这伴随着一个重要的警告:即使合法但falsy的值也会导致回退到默认值。
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-ish,它只会导致回退。这里的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
这样一来,就可以确保如果程序接受falsy值作为合法输入,最后就不会回退。
转载 注:
js
中falsy
值:''
,0
,-0
,NaN
,false
,null
,undefined
.
5. 逻辑分配
假设,当且仅当变量的值为null-ish时,才为该变量分配一个值。逻辑上的写法如下:
if (x === null || x == undefined) {
x = y
}
如果你知道JS短路的工作原理,则可能想要使用null-ish合并运算符将这三行代码改为更简洁的版本。
译者注:短路,即逻辑运算从左到右。逻辑或运算,当左边的条件成立时,后面的条件将不再参与运算。
x ?? (x = y) // x = y if x is nullish, else no effect
这里我们使用了null-ish合并运算符的短路功能来执行第二部分,如果x
为nullish,则x = y
。代码非常简洁,但理解起来仍然有点难度。逻辑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,那么你应该已经看到过
||=
和&&=
运算符,因为Ruby没有falsy值的概念。
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在创建时的状态为pending。很快,当API返回响应时,它将转换为fulfilled状态,并包装可以访问的Respond。在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 ... catch
与await
结合使用,而不是像直接使用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
关键字就是硬币的另一面,它包装了将在Promise中发送的所有数据。想一下假设我们要在以下异步函数中添加多个数字。当然,在现实世界中,代码执行的肯定是更为复杂的操作。
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