- 原文地址:levelup.gitconnected.com/9-tricks-fo…
- 原文做者:Lukas Gisder-Dubé
- Markdown 地址:github.com/wanghaiqion…
又一年过去了,JavaScript 也一直在改变。不过有些技巧能够帮助你写出简洁高效可伸缩的代码,即使是(或者说特别是)2019 年。下面 9 条实用小技巧能助你成为一个更好的开发者。javascript
若是你仍深陷回调地狱,那么你应该还在写 2014 年以前的老古董代码吧。除非颇有必要,好比遵照代码库要求或者出于性能缘由,不然不要使用回调方式。Promise 还行,但若是你的代码日渐庞大,Promise 就显得有些尴尬了。我如今的首选方案是 async / await
,它让代码的阅读与改进都变得简洁不少。事实上,你能够 await
JavaScript 中的每个 Promise,若是你用的库函数返回一个 Promise,就能够简单地 await
之。其实,async / await
只是使用 Promise 的语法糖。想让你的代码正常工做起来,你只须要在 funcion 前增长 async
关键字。以下是一个简单例子:前端
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
函数中使用。java
async / await
是在 ES2017 中引入的,因此记得转译你的代码。react
实际开发中不可避免地常常会遇到这种状况,咱们要获取多个数据项而后分别对它们进行某些处理(for…of
),或者须要在全部异步调用都获得返回值后再完成某项任务(Promise.all)。webpack
比方说咱们要获取页面中几个 Pokemon 的具体信息,咱们并不想等待全部调用所有完成,尤为是有时候并不知道具体有多少次调用,但咱们想只要一有返回数据就当即更新数据项。这时候咱们就能够用 for...of
来遍历数组,在循环体内执行 async 代码,代码的执行会被暂停,直到每次调用成功。必须注意的是若是你在代码中如示例这样作,可能会带来性能瓶颈,但把这个技巧收藏你的工具箱里仍是很是有用的。示例以下:ios
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)
复制代码
注:这些示例均可有效运行,可随意复制粘贴到你喜欢的代码沙盒内执行(如 jsfiddle、jsbin、codepen)。git
若是想并行获取全部 Pokemon 的信息又该如何实现呢?既然 await
能够用在全部 Promise 上,很简单,用 Promise.all
:github
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+ 引入的,因此(必要的话)记得转译你的代码。web
让咱们返回到上一示例中,咱们是这样作的:npm
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 values)和 假值
(falsy values)老是很是好的。它能帮助咱们改进代码,省去一些表达式,让代码更清晰明白。我常常看到有人这样作:
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(...)
}
复制代码
想要真正用好这些,你须要理解 真值
和 假值
的含义。这里有个小总结:
假值
0
false
undefined
null
NaN
真值
注意在检测真假值时,这里进行的是非严格比较,也就是说用的是 ==
而不是 ===
。通常说来,两者行为相同,但在某些特定状况下会出现 bug。对我来讲,常发生在数字 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
复制代码
咱们把逻辑运算符与上一个知识点真假值结合起来理解。当使用逻辑运算符时,听从以下规则:
&&
:返回第一个假值,若是没有,则返回最后一个真值||
:返回第一个真值,若是没有,则返回最后一个假值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 evening
复制代码
你是否遇到过这种问题,想要访问嵌套对象的属性,然而并不知道该对象或其中一个子属性是否存在?你极可能会写出相似这样的代码:
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
复制代码
我认为,这是一种让代码更清晰的检查嵌套属性的有效方法。
注:目前可选链式调用 (optional chaining) 还不是官方规范的一部分,是处于 stage-1 的实验性特性。你须要在你的 balelrc 中添加插件 @babel/plugin-proposal-optional-chaining 来使用。
函数绑定在 JavaScript 中十分常见。随着 ES6 规范中箭头函数的引入,咱们如今有办法自动绑定函数到定义时的上下文了,这种方法很是好用,被 JavaScript 开发者普遍使用。Class(类)刚刚引入的时候,你并不能真正的使用箭头函数,由于类方法须要一种特定的声明方式。咱们要在其余地方绑定函数,如在构造器中(React.js 的最佳实践)。我一直以为先定义类方法而后再绑定的流程很烦人,一段时候事后再看更感受莫名其妙。有了类属性语法,咱们又能够用箭头函数得到自动绑定的好处。箭头函数如今能够在类内使用了。示例以下,重点看 _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 })
}
}
复制代码
注:目前,class properties 并非正式官方规范的一部分,是处于 stage-3 的一个实验性特性。须要在你的 balelrc 中添加插件 @babel/plugin-proposal-class-properties 来使用。
作为前端开发者,你确定遇到过打包和转译代码的状况。wepback 成为事实标准已经有很长一段时间了。我最初使用 webpack 时它还处于第一个版本,那时候很痛苦。我花了无数个小时去处理各类不一样的配置项,让项目打包运行。一旦能跑起来,我就不再会去动它们,怕又给弄坏了。几个月前偶然发现的 parcel,让我松了口气。它提供的全部功能开箱即用,同时还容许咱们在必要时作出更改。它像 webpack 或者 babel 同样支持插件系统,而且速度极快。若是你还没听过 parcel,墙裂建议去看看!
这是个很好的话题。关于这个问题,我有过不少不一样的讨论。即便是 CSS,有不少人也会倾向于使用组件库,好比 bootstrap。JavaScript 的话,也有很多人使用 jQuery 和一些轻量代码库处理验证、滑动效果等。虽然用库也能够理解,但我仍是墙裂建议本身编写更多的代码,而不是盲目地安装 npm 包。对于那些整个团队维护构建的大型代码库(或者框架),如 moment.js、react-datepicker,咱们我的尝试去编写是没有什么意义的。但能够多写一些只是本身项目使用的代码。这样对本身有三大好处:
一开始,用 npm 包会显得更简单,本身去实现某些功能反而更费时间。但万一这个包并无像预期的那样工做,而后你不得不换另外一个,花更多的时间去阅读如何使用新的 API。若是是本身实现,你能够按本身的使用状况 100% 量身定制。