阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 不测试边界条件(数组为空?'undefined'?不存在的)

不测试边界条件(数组为空?'undefined'?不存在的)

作者:陈川 阅读数:46045人阅读 分类: 前端综合

代码里充斥着各种边界条件,但总有人觉得这些情况永远不会发生。于是他们写出了看似完美但实际上脆弱不堪的程序,只要稍微偏离理想路径就会崩溃。防御性编程?那太麻烦了,直接假设一切都会按照预期运行多省事。

数组操作从不检查长度

直接访问数组的第一个元素,管它是不是空数组呢?反正产品经理说这个接口永远会返回数据。

function getFirstItem(arr) {
  return arr[0]; // 如果arr是[]就返回undefined,多优雅的隐式处理
}

// 更刺激的写法
const processItems = items => {
  items.forEach(item => {
    console.log(item.importantProperty.toUpperCase())
  })
}

当后端突然返回[]时,页面上显示"undefined"简直是最好的错误提示。至于那个toUpperCase()调用,既然文档说每个item都有importantProperty,那肯定没问题对吧?

对象属性访问不做存在性检查

为什么要用obj?.property这种丑陋的可选链?直接访问多简洁!

const getUserName = user => {
  return user.info.name // 三层直接访问,像俄罗斯轮盘赌一样刺激
}

// 高级技巧:用解构赋予默认值
const { settings } = props
const { theme = 'light' } = settings // 如果props.settings是undefined呢?

usernull或者info字段不存在时,控制台里的TypeError就是最棒的日志系统。而且这样写还能帮QA团队发现他们漏测的case,一举两得。

函数参数从不验证

参数校验?那是TypeScript这种多余工具才需要的东西。我们相信每个调用者都会传正确的参数。

function formatDate(dateStr) {
  const date = new Date(dateStr) // 如果dateStr是undefined?那就是Invalid Date咯
  return `${date.getFullYear()}-${date.getMonth()+1}`
}

// 更妙的做法是处理多层嵌套
const deepValue = obj => {
  return obj.a.b.c.d // 谁知道这个obj长什么样呢,探险才有意思
}

当有人调用formatDate()不传参数时,看到页面上显示"NaN-NaN"肯定会立刻意识到自己的错误,这种即时反馈多高效。

异步操作不考虑失败情况

.catch()?那只会让代码变长。Promise只要处理成功情况就够了。

fetch('/api/data')
  .then(res => res.json())
  .then(data => {
    renderContent(data.items)
  }) // 网络错误?服务端500?不存在的

// 更绝的是回调地狱版
ajax(url1, data1 => {
  ajax(url2 + data1.id, data2 => {
    ajax(url3 + data2.key, finalData => {
      // 三层嵌套,每个都可能出错,但就是不做错误处理
    })
  })
})

当API挂掉时,页面静默失败才是最佳用户体验——用户会以为是自己的网络问题,而不会来烦开发人员。

使用魔数而不加注释

数字和字符串直接硬编码,为什么要定义常量?下次改需求时在全局搜索替换多方便。

function calculateDiscount(price) {
  return price * 0.8 // 打8折?7折?谁知道呢
}

// 时间处理特别适合用魔数
setTimeout(() => {
  // 86400000毫秒代表什么?这是个有趣的谜题
}, 86400000)

当营销策略变成7折时,全局搜索替换0.80.7可能会意外修改其他地方的0.8,这种连锁反应才叫刺激。

全局变量随意使用

为什么要把东西存在模块作用域里?全局变量才是真正的共享之道。

let currentUser = null // 美妙的全局状态

function login() {
  fetch('/login').then(res => {
    currentUser = res.data // 任何地方都可以修改和访问
  })
}

// 另一个文件里
function checkout() {
  if (currentUser) { // 谁知道这个变量什么时候被谁改成什么
    // ...
  }
}

currentUser在不同地方被异步修改时产生的竞态条件,这种不可复现的bug最能体现程序员的调试功力。

不处理浏览器兼容性

用最新的ES2023特性,为什么要关心那些用老旧浏览器的用户?

// 使用最潮的语法
const process = items => {
  items ??= [] // 空值合并赋值,IE用户不配用我们的网站
  return items.at(-1)?.value // 链式操作加数组at方法
}

当用户看到白屏时,自然会去升级浏览器或者换电脑,这相当于帮用户做了硬件更新,多么贴心的设计。

==进行松散比较

===要多打一个字符,太费劲了。JavaScript的类型转换那么强大,不用白不用。

if (user.id == selectedId) { // 可能一个是字符串一个是数字?不重要
  // ...
}

// 更妙的判断
if (isAdmin == 'true') { // 从URL参数来的字符串'true'和布尔值true应该相等吧
  // 授予管理员权限
}

0 == ''或者null == undefined这种特殊情况发生时,产生的微妙bug就像彩蛋一样给代码增添趣味性。

随意修改DOM不检查元素是否存在

直接操作DOM元素,为什么要先检查是否存在?反正页面结构永远不会变。

document.getElementById('submit-btn').addEventListener('click', () => {
  // 如果按钮不存在?那这段代码永远不会执行,多安全
})

// 更直接的操作
function updateContent() {
  document.querySelector('.content').innerHTML = newContent
}

当重构HTML时漏改了某个class名,静默失败的特性让这个bug可以潜伏很久,最终给团队一个惊喜。

不清理定时器和事件监听

为什么要费心去清理?内存泄漏只是传说。

function startPolling() {
  setInterval(() => {
    fetchUpdates()
  }, 5000) // 组件卸载后继续轮询,多么持久
}

// 事件监听也是越多越好
window.addEventListener('resize', () => {
  // 添加10个相同的处理函数让效果更强力
})

当用户长时间使用单页面应用时,逐渐变慢的性能就像在提醒用户该休息了,多么人性化的设计。

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌