原文连接:9 Tricks for Kickass JavaScript Developers in 2019
原文做者:Lukas Gisder-Dubé
译者:JintNiu
推荐理由:JavaScript
已经成为了当今使用最为普遍、最受欢迎的语言之一,掌握一些使用技巧不只能够提升开发效率,更有利于思惟转换。javascript
过去的一年, JavaScript
在持续变化着,其使用范围也愈来愈广。接下来,我将针对 JavaScript
的使用,列出 9 条 建议,以帮助你写出更加整洁高效的代码,成为更好的开发者。php
JavaScript
极速发展的今天,回调地狱所产生的问题已不复存在。实际开发过程当中咱们应当尽可能避免使用回调函数,除非为了遵照代码库规则或是维护性能。而解决回调地狱的一个经常使用方法为 Promise
,但在代码量较多时使用会拔苗助长。因而提出了 async / await
,使代码结构更加清晰明了,便于阅读和维护。通常而言,能够 await
任何 Promise
以防止正使用的库的返回值为 Promise
,也就是说 async/await
是 Promise
的语法糖,并且使用方法也十分简单:在函数前加 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
当咱们进行异步调用并得到返回值时,一般指望直接获取多个数据集,而且分别操做每一个数据集。所以有了如下方式:react
假设页面上要展现 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
地狱,影响性能。
若是想要并行获取全部的 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...of
和Promise.all
都是ES6+
引入的,使用时请进行转换。
回到上个例子:
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
)和虚值(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(...)
}
复制代码
如下为 Falsy
和 Truthy
的概念:
Falsy
0
false
undefined
null
NaN
Truthy
使用真值和虚值时没有确切的比较方式,这相似于咱们进行比较时常使用双等号 ==
而不是三等号 ===
。通常而言,这二者的断定方式相同,但在某些状况下也会遇到一些错误,对我来讲主要为数字 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
复制代码
咱们能够将逻辑运算符与真值和虚值的相关知识结合起来。
若是有表达式 A
和 B
,针对两种逻辑运算符,有如下规则:
A && B
: 当 A
为 false
时则直接返回 A
的值 ;不然返回 B
的值。A || B
: 当 A
为 true
时则直接返回 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
复制代码
三元运算符与逻辑运算符很是类似,但有由三个部分组成:
例如:
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
复制代码
当访问某个嵌套对象的属性时,因为不能肯定目标对象或者属性性是否存在,而须要进行一系列判断:
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
后方可以使用它。
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
后方可以使用。
做为前端开发人员,保证会有打包项目或着转换代码的需求,对此,webpack 已经在好久以前提出先关规范了。第一次使用 webpack v1.0 时,我花了很长时间进行配置,虽然最终运行成功,但整个过程很是痛苦,并且成功后的我变得畏手畏脚,生怕破坏以前的配置。直到几个月前,ParcelJS
的发现使我心情大好,在提供开箱即用功能的同时,它还实现了按需配置,也能够支持相似于 webpack 或 babel 的插件系统,最重要的是它的速度极快。
译者注:ParcelJS 官网显示,parcelJS 的打包速度比 webpack 快 2 倍以上。
这是一个很是有趣的话题,关于它我有不少的想法。对于 CSS
,不少人更倾向于使用相似于 BootStrap 这样的组件库。而对于 JavaScript
,仍然有人调用 jQuery
或者其余库来实现验证、滑块等功能。首先不否定使用各类库的好处,但仍是强烈建议能够亲手实现这些功能,而不是盲目地安装 npm 包。当整个团队正构建一个相似于 moment.js 或 react-datepicker 的大型库(甚至框架)时,你不必亲手实现它,但能够封装为属于本身的组件库,并且在实现组件库的同时,您能够:
直接使用 npm 包是固然很是容易,但若是想要实现某些 npm 包中不具有的功能时则会须要更多的时间:若是软件没有按预期正常工做,或者要将其转换为另外一个软件包,您将会花费更多时间来了解其 API 的配置方式。所以,您能够为本身量身定作一套数据本身的组件库。
关于做者:Lukas Gisder-Dubé 组件并领导了一家初创公司,期间创建了本身的技术团队,并任职 CTO 一年半。 离开创业公司后,在 Ironhack 担任首席讲师。现在在柏林正创建一家创业咨询公司。查看 dube.io 以了解更多信息。