总结下对 JavaScript 代码编写的认识,写尽可能优雅的代码。
代码技巧
- ES2015+ 新特性写法
熟练使用 ES6 新特性可以优化代码,更加简洁,代码对比
// 箭头函数
function foo(){
console.log('hello world')
}
const foo = () => console.log('hello world')
// 数组去重
const formatArray = arr => [...new Set(arr)]
// 数组合并
const newArr = [...arr1, ...arr2, 'value']
// 对象浅拷贝
const newObj = {...obj}
// 解构赋值
const person = {name: 'bao', age: 18}
const { name, age } = person
// 常见对象属性取值前判断对象是否存在
// 如下是 react + antd 渲染 table 的简单代码,对象存在性判断 + 默认值设置
render(){
const { downloadList } = this.props.store.downloadList
let items = downloadList && downloadList.items || []
let itemCount = downloadList && downloadList.itemCount || 10
return <Table dataSource={items} pagination={{total: itemCount}} />
}
// 优化后
render(){
const { items, itemCount } = this.props.manageStore.downloadList || {}
return <Table dataSource={items || []} pagination={{total: itemCount || 10}}/>
}
- 优化逻辑判断语句
大量的 if else 逻辑判断难以维护,且性能较差,可用多种方式代替
// 对象配置法
// 函数内部有条件判断,且 return 值时,满足条件立即return,而不要在结尾return
const foo = v => {
if (v === 'name') {
return 'bao'
} else if (v === 'age') {
return '18'
} else if (v === 'height') {
return '180'
}
}
const cfg = {
name: 'bao',
age: '18',
height: '180'
}
const foo = v => cfg[v]
// 数组配置法
if (value === 'hello' || value === 'world' || value === 'blabla') {
// ...
}
// 配置数组形式
const rightValue = ['hello', 'world', 'blabla']
if (rightValue.includes[value]) {
// ...
}
- 善用 && 、 || 和 三元运算
if (name === 'bao') {
someFunc()
}
name === 'bao' && someFunc()
if (name === 'bao') {
someFunc()
} else {
elseFunc()
}
name === 'bao' ? someFunc() : elseFunc()
- 对象属性变量应用
如在 react 中,调用 action 方法来获取数据,不同条件执行不同方法
if (isMember) {
let res = await actions.getMemberInfo(params)
} else {
let res = await actions.getCommonUserInfo(params)
}
const actionName = isMember ? 'getMemberInfo' : 'getCommonUserInfo'
let res = await actions[actionName](params)
- 类型转换
// 字符串转数字
let str = '1234'
let num = +str
console.log(+new Date()) // 1536231682006
// 转字符串
let str = `${num}`
- 用 Array.map(), Array.filter() 代替数组 for 循环实现简易写法
如下对数组元素的操作
let arr = [1, 2, 3, 4, 'A', 'B']
// 1. 取出 arr 中数字项为新数组
let numArr = []
for(let i in arr){
if(typeof arr[i] === 'number'){
numArr.push(arr[i])
}
}
// 改用filter
let numArr2 = arr.filter(item => typeof item === 'number')
console.log(numArr2) // [1,2,3,4]
// 2. 获得新数组,元素是 arr 每个元素作为 value, key 为 arr 下标的对象, 不修改 arr
let strArr = []
for(let i in arr){
strArr.push({[i]: arr[i]})
}
// 改用 map
let strArr2 = arr.map((item, i) => ({[i]: arr[i]}))
console.log(strArr2) // [ { '0': 1 },{ '1': 2 },{ '2': 3 }, { '3': 4 }, { '4': 'A' }, { '5': 'B' } ]
- 浅拷贝、深拷贝 复杂数据类型对象深拷贝建议使用库来实现,如 lodash.cloneDeep
// 浅拷贝
let obj1 = { a: 11, b: { ba: 22 } }
let obj2 = {...obj1}
console.log(obj2); // { a: 11, b: { ba: 22 } }
console.log(obj1 === obj2); // false
console.log(obj2.b === obj1.b); // true
// 深拷贝,这种方法需要对象能够被 json 序列化
let obj3 = JSON.parse(JSON.stringify(obj1))
console.log(obj3); // { a: 11, b: { ba: 22 } }
console.log(obj3===obj1); // false
console.log(obj3.b===obj1.b); // true
- optional-chaining ?. 代替对象是否存在判断
需要添加 @babel/plugin-proposal-optional-chaining 插件支持,可参考babel 7 简单升级指南
// obj?.prop // optional static property access
// obj?.[expr] // optional dynamic property access
// func?.(...args) // optional function or method call
// 如下代码
let { data } = this.props
let list = data && data.tableData && data.tableData.list || []
// 使用 ?.
let list = data?.tableData?.list || []
更高效的代码
- 使用局部变量代替引用类型查找
局部变量的读取速度最快,而引用类型的数据读取需要按引用指针去查找,所以可以对多次使用的引用类型属性 使用局部变量读取一次,重复使用
let obj = {
person: {
man: {
bao: {
age: 18
}
}
}
}
let age = obj.person.man.bao.age
// use age do many things
- 删除多个对象属性时先使属性为 null
删除属性时,js 引擎会去查找该属性的值是否是其他对象的引用,所以删除前提前赋值为 null,可以减少 js 引擎的检测过程,提高效率
let obj = {
person: {
man: {
bao: {
age: 18
}
}
}
}
obj.person = null
delete obj.person
- 局部变量保存数组 length
关于这个优化的讨论一直不少,参考有没有必要缓存JavaScript数组的length
let len = arr.length
for(let i=0; i<len; i++)){
// ...
}
代码结构组织等优化
随着项目(react 项目)的日益扩大,代码量快速增多,后期维护非常耗费时间,主要通过以下方法减少代码量
- 凡是二次出现的代码片段,立即考虑复用,拆分公共组件
- 封装功能函数
最近做了一个复杂的图表、表格数据交互功能,涉及表格数据的各种计算、排序、比较、编辑前后对比等功能, 逻辑复杂,按交互不同有多种逻辑执行过程。采取的方法是单一功能函数封装,对每个计算、数据处理步骤, 都封装为小函数,不同逻辑不同函数组件组合,保证每个函数的可用性,整体功能也可以保证质量。
同时可以将项目常用的功能方法封装公用 util 来复用使用
- 善用 class 类的继承,复用功能方法 主要封装了 fetch 请求的 get/post 方法到 CommonActions class, 然后创建 actions 时只需要 extends CommonActions 就可以在新的 actions 中直接调用 this.get()/this.post() 来完成 请求数据。
总之,在逻辑清晰的情况下,尽可能复用组件、方法,维护好高质量的公共组件、方法,便于项目维护
- 善用装饰器 react 高阶函数已经提供了一种优雅的组件复用形式,而装饰器的使用可以更优雅地实现高阶函数 如我的另一篇记录 React Error Boundary 组件异常处理方案实践
同时,ant design Form 组件的创建也可用装饰器更简单的实现
class MyForm extends React.Component {
// ...
}
const WrapForm = Form.create()(MyForm)
// 其他组件使用 WrapForm 组件
// 装饰器形式
@Form.create()
class MyForm extends React.Component {
// ...
}
编不下去了,暂时就想起来这么多,想起来了再添加…