1.16 链判断运算符
编程实务中,如果读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在。比如,读取message.body.user.firstName这个属性,安全的写法是写成下面这样。
// 错误的写法 const firstName = message.body.user.firstName || 'default'; // 正确的写法 const firstName = (message && message.body && message.body.user && message.body.user.firstName) || 'default'; // 但是这种写法如果层级过长会显得冗长,可读性也不强,这是我们可以利用链判断运算符来解决 //链判断运算符写法 const firstName = message?.body?.user?.firstName || 'default';
链判断运算符?.有三种写法。
- obj?.prop // 对象属性是否存在
- obj?.[expr] // 同上 也可以理解为 arr?.[index]
- func?.(...args) // 函数或对象方法是否存在
下面是?.运算符常见形式,以及不使用该运算符时的等价形式。
a?.b // 等同于 a == null ? undefined : a.b a?.[x] // 等同于 a == null ? undefined : a[x] a?.b() // 等同于 a == null ? undefined : a.b() a?.() // 等同于 a == null ? undefined : a()
常规:
// 计划 id let planId = this.ticketDetail && this.ticketDetail.planDetailVoList && this.ticketDetail.planDetailVoList[0] && this.ticketDetail.planDetailVoList[0].planId
简写:
// 计划 id let planId = this.ticketDetail?.planDetailVoList?.[0]?.planId // 如果任意一个问号前面的值为null或者undefined都将直接返回undefined,不在向下取值
注意:链判断运算符只能在js代码块中使用,不能在template里面的标签上使用,否则会解析报错,如果想要在template里达到上述简写效果可以在计算属性中使用。
// 错误的写法 <template> <div class="wrap"> <span>计划编号:</span> {{ ticketDetail && ticketDetail.planDetailVoList && ticketDetail.planDetailVoList[0] && ticketDetail.planDetailVoList[0].planNo || '' }} </div> </template> <script> export default { data() { return { ticketDetail: { planDetailVoList: [ { planNo: 'T202304200940' // 计划编号 } ] } } } } </script> //正确的写法 <template> <div class="wrap"> <span>计划编号:</span> {{ planNo }} </div> </template> <script> export default { data() { return { ticketDetail: { planDetailVoList: [ { planNo: 'T202304200940' // 计划编号 } ] } } }, // 计算属性 computed: { // 计划编号 planNo({ticketDetail}) { return ticketDetail?.planDetailVoList?.[0]?.planNo || '' } } } </script>
链判断运算符知识点链接:https://es6.ruanyifeng.com/#docs/operator
1.17 Null 判断运算符
读取对象属性的时候,如果某个属性的值是null或undefined,有时候需要为它们指定默认值。常见做法是通过||运算符指定默认值。
const headerText = response.settings.headerText || 'Hello, world!'; const animationDuration = response.settings.animationDuration || 300; const showSplashScreen = response.settings.showSplashScreen || true;
上面的三行代码都通过||运算符指定默认值,但是这样写是错的。开发者的原意是,只要属性的值为null或undefined,默认值就会生效,但是属性的值如果为空字符串或false或0,默认值也会生效。
为了避免这种情况,ES2020 引入了一个新的 Null 判断运算符??。它的行为类似||,但是只有运算符左侧的值为null或undefined时,才会返回右侧的值。
const headerText = response.settings.headerText ?? 'Hello, world!'; const animationDuration = response.settings.animationDuration ?? 300; const showSplashScreen = response.settings.showSplashScreen ?? true;
上面代码中,默认值只有在左侧属性值为null或undefined时,才会生效。
1.18 逻辑运算符
使用逻辑运算符!! 快速进行布尔转换。
常规:
// 判断是否是ios环境 const isIOS = Boolean(window.navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/))
简写:
// 判断是否是ios环境 const isIOS = !!window.navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
1.19 扩展运算符
使用扩展运算符进行数组、对象合并。
常规:
let obj = { name: 'zhangsan', sex: '男' } obj = Object.assign(obj, {age: 18}) let arr = ['zhangsan', 'lisi'] arr = arr.concat(['wangwu', 'zhaoliu'])
简写:
let obj = { name: 'zhangsan', sex: '男' } obj = {...obj, ...{age: 18}} obj = {...obj, age: 18} let arr = ['zhangsan', 'lisi'] arr = [...arr, ...['wangwu', 'zhaoliu']]
1.20 +运算符隐式转换
使用+运算符不仅可以将字符串转为数字,还可以将时间转为时间戳。
常规:
let type = '1' type = Number(type) let sjc = new Date().getTime()
简写:
let type = '1' type = +type let sjc = +new Date()
1.21 v-for中使用解构
常规:
<li v-for="item in users" :key="item.id" > {{ item.name }} </li>
简写:
<li v-for="{ name, id } in users" :key="id" > {{ name }} </li>
1.22 使用Set给数组去重
常规:
Array.prototype.distinct = function(){ let arr = this, result = [], i, j, len = arr.length; for(i = 0; i < len; i++){ for(j = i + 1; j < len; j++){ if(arr[i] === arr[j]){ j = ++i; } } result.push(arr[i]); } return result; } const arra = [1,2,3,4,4,1,1,2,1,1,1]; arra.distinct(); // [3,4,2,1]
简写:
const arr = [1, 1, 2, 3, 4, 4]; const uniqueArr = [...new Set(arr)]; const uniqueArr1 = Array.from(new Set(arr)); // [1,2,34]
1.23 Object[key] 重用代码块
常规:
function validate(values) { if(!values.first) { uni.showToast({ title: '第一项不可为空', icon: 'none' }) return false } if(!values.last) { uni.showToast({ title: '最后一项不可为空', icon: 'none' }) return false } return true } let isCheckPass = validate({first:'', last: ''}) console.log('是否检验通过', isCheckPass)
简写:
// 对象校验规则 const schema = { first: { required: true, failedTip: '第一项不可为空' }, last: { required: true, failedTip: '最后一项不可为空' } } // 通用校验函数 const validate = (schema, values) => { for(field in schema) { if(schema[field].required) { if(!values[field]) { uni.showToast({ title: schema[field].failedTip, icon: 'none' }) return false } } } return true } let isCheckPass = validate(schema, {first: '1', last: '3'}) console.log('是否检验通过', isCheckPass)
1.24 禁用不必要的嵌套块
常规:
function submitTicket(){ const { ticketCode, isDoubleSign } = this if(ticketCode === '05') { if(isDoubleSign) { this.signType = '02' } } }
简写:
function submitTicket(){ const { ticketCode, isDoubleSign } = this if(ticketCode === '05' && isDoubleSign) { this.signType = '02' } }
1.25 具有默认值的函数参数应该放到最后
默认值放到最后可以让函数少传实参还能正常执行,收获预期结果。
常规:
求和函数,把具有默认值的参数放在参数列表「左边」
function sum(a = 10, b) { return a + b } /* 第1个实参 总是对应 第1个形参 所以,3 赋值给 a, 替换掉默认值 10 参数b没有传值,最终函数返回NaN */ sum(3) // returns NaN as b is undefined
简写:
求和函数,把具有默认值的参数放在参数列表「右边」
function sum(b,a = 10) { return a + b } /* 3 赋值给 b a 没有传值,使用默认值 10 */ sum(3) // 13
1.26 组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法
Bad Code
<template> <div class="wrap"> <span>工作负责人:</span> {{ planDetail.personInfo.name + (planDetail.personInfo.phone? '-' + planDetail.personInfo.phone : 'planDetail.personInfo.phone') }} </div> </template> <script> export default { data() { return { planDetail: { personInfo: { name: 'zhangsan', phone: '15236616216' } } } } } </script>
Good Code
<template> <div class="wrap"> <span>工作负责人:</span> {{ WorkLeader }} </div> </template> <script> export default { data() { return { planDetail: { personInfo: { name: 'zhangsan', phone: '15236616216' } }, } }, conputed: { // 复杂的表达式则应该重构为计算属性 WorkLeader({planDetail}) { return planDetail.personInfo.name + (planDetail.personInfo.phone? '-' + planDetail.personInfo.phone : planDetail.personInfo.phone) } } } </script>
注意:计算属性中必须包含return字段,且不能产生副作用(如修改外部变量,调用外部无关方法操作了dom等等)
1.27 计算属性使用解构赋值减少this滥用,提升性能
Bad Code
<script> export default { data() { return { ticketDetail: { wtType: '03' }, sendFlag: false, provPermitType: '' } }, computed: { // 票类型 wtType() { return this.ticketDetail.wtType }, // 应采取安全措施是否可编辑 measureEdit() { // 配一工作票 非远程许可 非发送 可编辑 if (this.ticketDetail.wtType === "03" && !this.sendFlag && !['01', '02'].includes(this.provPermitType)) { return true } return false } } } </script>
上述计算属性中存在滥用this去读取data数据的问题 , 使用this去读取data中数据时会去收集依赖,如果滥用this去读取data中数据,会多次重复地收集依赖,从而产生性能问题。
解决办法:
计算属性的值是一个函数,其参数是Vue的实例化this对象,在上述计算属性中滥用this的例子中可以这样优化。
Good Code
<script> export default { data() { return { ticketDetail: { wtType: '03' }, sendFlag: false, provPermitType: '' } }, computed: { // 票类型 wtType({ticketDetail}) { return ticketDetail.wtType }, // 应采取安全措施是否可编辑 measureEdit({wtType, sendFlag, provPermitType}) { // 配一工作票 非远程许可 非发送 可编辑 if (wtType === "03" && !sendFlag && !['01','02'].includes(provPermitType)) { return true } return false } } } </script>