都2022了,不会还有人不会用es6吧,不会吧不会吧
直击痛点
痛点1:if else直接拉满
当你接手一个别人开发新项目时,总会遇到页面
if (xxx) { //做些什么 } else if (xxx) { //做些什么 } else if (xxx) { //做些什么 } else if (xxx) { //做些什么 } else if (xxx) { //做些什么 } else if (xxx) { //做些什么 } else if (xxx) { //做些什么 } else { //做些什么 }
一大坨,我有代码洁癖,看着难受
优化方案1:switch
switch (status){
case xxx:
//做些什么
break
case xxx2:
case xxx3:
//做些什么
break
case xxx:
//做些什么
break
case xxx:
//做些什么
break
default:
//做些什么
break
}
注意:
case xxx2
和case xxx3
逻辑完全一毛一样,这时可以省去执行语句
和break
, 然后case xxx2
的情况自动执行case xxx3
的逻辑。
优化方案2:Object
const typeObj = {
'xxx1': '可以是字符串值',
'xxx2': ['可以是数组值1','可以是数组值2'],
'xxx3': { a: '可以是对象值' },
'xxx4': () => { //可以是回调函数来做些什么, 可以写普通function, 这里使用es6箭头函数 },
'default': 'xxx'
}
const status = '状态-对应typeObj的属性'
const typeVal = typeObj[status] || typeObj['default']
注意:
把判断条件
作为对象的属性名
,将处理逻辑
作为对象的属性值
,在执行操作的时候,通过对象属性查找
的方式来进行逻辑判断
,这种写法特别适合一元条件判断的情况。
那多元条件判断怎么写呢???人狠话不多直接上代码。
// 多元if else嵌套
if (type === 'aaa') {
if (status === 1) { //做些什么 }
if (status === 2) { //做些什么 }
if (status === 3) { //做些什么 }
if (status === 4) { //做些什么 }
} else if (type === 'bbb') {
if (status === 1) { //做些什么 }
if (status === 2) { //做些什么 }
if (status === 3) { //做些什么 }
if (status === 4) { //做些什么 }
}
// 优化写法
const typeObj = {
'aaa1': '可以是字符串值',
'aaa2': ['可以是数组值1','可以是数组值2'],
'aaa3': { a: '可以是对象值' },
'aaa4': () => { //可以是回调函数来做些什么, 可以写普通function, 这里使用es6箭头函数 },
'bbb1': '可以是字符串值',
'bbb2': ['可以是数组值1','可以是数组值2'],
'bbb3': { a: '可以是对象值' },
'bbb4': () => { //可以是回调函数来做些什么, 可以写普通function, 这里使用es6箭头函数 },
'default': 'xxx'
}
const text = 'aaa/bbb',
const num = '状态-对应1、2、3、4'
const typeVal = typeObj[`${text}${num}`] || typeObj['default']
这里多bb一嘴…可以直接略过此注意项
在使用 JSON.parse(JSON.stringify(xxx))时应该注意一下几点:
JSON.parse(JSON.stringify())复制时间对象、Error对象、正则表达式,函数,或者undefined等值,此方法就会出现问题
1.如果json里面有时间对象,则序列化结果:时间对象=>字符串的形式;
2.如果json里有RegExp、Error对象,则序列化的结果将只得到空对象 RegExp、Error => {};
3.如果json里有 function,undefined,则序列化的结果会把 function,undefined 丢失;
4.如果json里有NaN、Infinity和-Infinity,则序列化的结果会变成null;
5.如果json里有对象是由构造函数生成的,则序列化的结果会丢弃对象的 constructor;
6.如果对象中存在循环引用的情况也无法实现深拷贝
优化方案3:Map (终于可以写es6了,巨鸡冻)
不知道ES6-Map的看我之前的文章es6 Set 和 Map 数据结构
和Object方式很像, 都是键值对的形式
const typeMap = new Map([
['xxx1': '可以是字符串值'],
['xxx2': ['可以是数组值1','可以是数组值2']],
['xxx3': { a: '可以是对象值' }],
['xxx4': () => { //可以是回调函数来做些什么, 可以写普通function, 这里使用es6箭头函数 }],
['default': 'xxx']
])
const status = '状态-对应typeMap的键'
const typeVal = typeMap.get(status) || typeMap.get('default')
Map的多元判断和Object写法差不多
// 多元if else嵌套
if (type === 'aaa') {
if (status === 1) { //做些什么 }
if (status === 2) { //做些什么 }
if (status === 3) { //做些什么 }
if (status === 4) { //做些什么 }
} else if (type === 'bbb') {
if (status === 1) { //做些什么 }
if (status === 2) { //做些什么 }
if (status === 3) { //做些什么 }
if (status === 4) { //做些什么 }
}
// 优化写法
const typeMap = new Map([
['aaa1': '可以是字符串值'],
['aaa2': ['可以是数组值1','可以是数组值2']],
['aaa3': { a: '可以是对象值' }],
['aaa4': () => { //可以是回调函数来做些什么, 可以写普通function, 这里使用es6箭头函数 }],
['bbb1': '可以是字符串值'],
['bbb2': ['可以是数组值1','可以是数组值2']],
['bbb3': { a: '可以是对象值' }],
['bbb4': () => { //可以是回调函数来做些什么, 可以写普通function, 这里使用es6箭头函数 }],
'default': 'xxx'
]
const text = 'aaa/bbb',
const num = '状态-对应1、2、3、4'
const typeVal = typeMap.get(`${text}${num}`) || typeMap.get('default')
这里也再多bb一嘴…
Map
对象和Object
对象有什么区别呢?
- 对象都有自己的原型,所以对象总有一个prototype键。
- 对象的键只能是字符串或者Symbols,但一个Map的键可以是任意值。
- Map可以用任何类型的数据作为key
- Map通过size属性得到键值对个数,而对象的键值对可以通过Object.keys()、Object.values() 去获取对应数组。
若是觉得查询条件
拼成字符串
有点别扭?? 不要慌,办法总比困难多,还有一种办法: 使用Map,然后用Object作为key
,不多bb,直接上代码
// 多元if else嵌套
if (type === 'aaa') {
if (status === 1) { //做些什么 }
if (status === 2) { //做些什么 }
if (status === 3) { //做些什么 }
if (status === 4) { //做些什么 }
} else if (type === 'bbb') {
if (status === 1) { //做些什么 }
if (status === 2) { //做些什么 }
if (status === 3) { //做些什么 }
if (status === 4) { //做些什么 }
}
const typeMap = new Map([
[{type: 'aaa', status: 1}, () => { //做些什么 }],
[{type: 'aaa', status: 2}, () => { //做些什么 }],
[{type: 'aaa', status: 3}, () => { //做些什么 }],
[{type: 'aaa', status: 4}, () => { //做些什么 }],
[{type: 'bbb', status: 1}, () => { //做些什么 }],
[{type: 'bbb', status: 2}, () => { //做些什么 }],
.......懂了吧,get到了下面就不写了太罗嗦了
])
const text = 'aaa/bbb',
const num = '状态-对应1、2、3、4' // 不要去纠结上面status值是数字,num和status不相等,我只是说明所以没有写对应的值(杠精请绕道,慢走不送!!!)
const typeVal = [...typeMap].filter(([key, val]) => key.type === text && key.status === num))[0][1]()
若条件再有很多,还有很多种条件做相同的事情, 那就只能使用正则
了
const typeMap = new Map([
[/^aaa[1-4]$/, () => { //做些什么 }],
[/^bbb[1-3]$/, () => { //做些什么 }],
[/^bbb4$/, () => { //做些什么 }],
.......懂了吧,get到了吧
])
const text = 'aaa/bbb',
const num = '状态-对应1、2、3、4' // 不要去纠结上面status值是数字,num和status不相等,我只是说明所以没有写对应的值(杠精请绕道,慢走不送!!!)
const typeVal = [...typeMap].filter(([key, val]) => key.test(`${text}${num}`)))[0][1]()
痛点2: 判断一些不确定的值
优化方案1: ??空值合并运算符
大家可能遇到过,如果一个变量是空,需要给它赋值为一个默认值的情况。通常我们会这样写:
const num = number || 1
但是,以上的代码会有一个 bug。如果number的值是0,则会被当作取不到其值,会取到'无法获取'这个字符串。
如果想要做到这一点,在这之前就只能使用三元运算符来实现:
const num = (number !== undefined) ? number : 1
现在可以使用??了,它只有当操作符左边的值是null或者undefined的时候,才会取操作符右边的值:
const num = number ?? 1
这里多bb一句…
逻辑赋值操作符 ??=、&&=、 ||=
// 等同于 a = a || b
a ||= b;
// 等同于 c = c && d
c &&= d;
// 等同于 e = e ?? f
e ??= f;
当我们要获取后台接口json数据,但是后台说什么什么属性不一定存在,有不确定性,然后我们前端的代码就写成了一坨…
// 此处省略ajax请求,懒得不想写,这玩意应该都会写
.....
// 假设接口返回的json数据为res
const res = {
code: 200,
data: {
userInfo: {},
options: {
id: 1,
title: '',
url: ''
}
},
message: 'success'
}
// 然后这里咱们要使用 options的内容
// 但是后台的接口写的很辣鸡 说 options可能会不存在 或者 options里的 title可能会不存在
// 这时候我们的代码只能 写一坨if判断条件
eg:
if(res.data && res.data.options && res.data.options.title) {
//做些什么
如果数据层次嵌套更多......我们就 res.data && res.data.options && res.data.options.title && ......
就很无语.......
}
当我们需要尝试访问某个对象中的属性或方法而又不确定该对象是否存在时
eg: xxx假设是一个div盒子, 我要访问它的宽度
const xWidth = xxx ? xxx.width : ''
还有其他一些场景也会遇到类似的问题,此处只是列举两个最常见的。
优化方案2:?.可选链接运算符
我们一般判断一个对象是否有某个属性使用 !==undefined
、in运算符
、hasOwnProperty()
、propertyIsEnumerable()
这里我么使用es6新增的?.
运算符:
?.链判断运算
?.运算符,直接在链式调用的时候判断,左侧的对象是否为null
或undefined
。如果是的,就不再往下运算,而是返回undefined。
常用的使用场景:
- 属性访问: 需要获取某个对象中的属性
a?.b a?.[c] 上面的代码中,如果 a 为undefined或null,则表达式会立即返回undefined,否则返回所访问属性的值。 也就是说,它们与下面这段代码是等价的: a == null ? undefined : a.b a == null ? undefined : a[c]
- 方法调用: 在尝试调用某个方法时,也可以使用
a?.() 同样是如果 a 为undefined或null,则返回undefined,否则将调用该方法。 不过需要额外注意的是,该操作符并不会判断 a 是否是函数类型,因此如果 a 是一个其它类型的值,那么这段代码依然会在运行时抛出异常。
- 访问深层次属性: 在访问某个对象较深层级的属性时,也可以串联使用
可能有人会懒得先去判断是否真的有必要,就给访问链路中的每个属性都加上该操作符。但类似上面代码中所展示的那样,这种代码可读性比较差。而且若真的有一个应当存在的对象因为某些 bug 导致它没有存在,那么在访问它时就应当是抛出异常,这样可以及时发现问题,而不是使它被隐藏起来。建议只在必要的时候才使用可选链操作符。a?.b?.[0]?.()?.d
const res = {
code: 200,
data: {
userInfo: {},
options: {
id: 1,
title: '',
url: ''
}
},
message: 'success'
}
// ?.的使用
if(res?.data?.options?.title) {
//做些什么
}