开发工作中遇到了许多的 💩⛰ 代码,这个系列里就大家分享&吐槽一下。
同时也分享一下最近深度使用的专业编程显示器👨🏻💻👍🏻 🖥
攒够素材就更新下一期,有好的素材也欢迎投稿,推荐!
1 random了寂寞
目的是将目标值和一个[0,100)的随机数比较,但结果一直是 FAILED。
让我们来看一下现在实现的代码 ↓:
function can(compareTo) { return Number.parseInt(Math.random * 100) > compareTo ? Status.SUCCESS : Status.FAILED }
💩 问题:Math.random,没有被调用,所以判断执行值始终是 NaN。
最终生效判断代码如下:
function can(compareTo) { return NaN > compareTo ? Status.SUCCESS : Status.FAILED }
NaN 与其它值进行 ><= 运算都是 false 所以这里逻辑不符合预期。
遇到这种💩,你觉得修还是不修!
一点补充:这段代码跑了好多年了,说明这个错误的执行结果符合现状。
2 执行一次的定时器
使用定时器的场景很常见,比如延后执行一次逻辑:
但看到下面这种代码你说难受不难受?
const timer = setTimeout(() => { clearTimeout(timer) console.log('exec once') }, 1000)
💩问题: setTimeout,本身就只执行一次,所以这里的 clearTimeout 多余。
const timer = setInterval(() => { clearInterval(timer) console.log('exec interval once') }, 1000)
💩问题: setInterval,用于循环执行,这种执行一次的场景,建议使用 setTimeout 。
代码功能没问题,但对于有代码洁癖的来说,看着比较难受。
很多仓库都有这个💩,不知道是谁带的头!
3 非必要的 async
经常看到一些方法,内部就只是同步逻辑,但不知道是什么坏习惯性加了 async
这会导致返回内容始终为一个 Promise
async function test() { return 'hello' }
调用的时候同步取值就需要 await ,同时方法本身也需要被迫添加 async!
💩 问题:容易破坏存量代码结构,甚至影响执行顺序。
下面就是同步和异步的执行结果区别

这种坑还是少一点好。
4 非必要的判断
返回值是 boolean 的时候,通常可以简化掉相关的判断。
来看看 bad case:
function case1() { if (xx) { return true } return false } function case2() { return xx ? true : false }
💩 问题:判断条件执行结果本来就是 boolean,不需要再多此一举。
function case() { return 判断条件 }
我相信大部分同学都遇到过这种冗余的判断,如果是为了凑代码量,那我建议多写注释。
5 冗余的else-if
一个取配置的场景:从不同的配置对象中取出同一意义的值
function getConfigValue(type, cfg) { if (type === 'xxx') { return cfg.id } else if (type === 'yyy') { return cfg.key } else if (type === 'zzz') { return cfg.secret } // 此处省略数十个判断。。。 }
💩 问题:重复浓度过高,一屏都是这种 else-if
个人倾向这种场景做成配置化,便于拓展,不用频繁改代码。
const config = { xxx: 'id', yyy: 'key', zzz: 'secret', } function getConfigValue(type, cfg) { return cfg[config[type]] }
猜测写第一版的人可能只写了几个 else-if 然后后面修改的人就不断的 CV,才导致现在这样冗长。
6 假同步执行
页面上有个功能时好时坏,让我们看看怎么回事 🤔。
看下代码的调用:
async function mounted() { await getProductList() await getUserInfo() await getUserTags() // 此处省略其它处理代码 } mounted()
大家不妨先按经验推测一下,可能原因,为什么一段代码功能会时好时坏?
下面揭晓一下
const data = {} async function getProductList() { request('productList').then((res) => { data.list = res }) } function getUserInfo() { return userSdk.getUserInfo() } async function getUserTags() { request('userTags', { ids: data.list.map(item => item.id) }).then((res) => {}) }
💩 问题: 异步方法返回内部没有等待内部逻辑执行完就提前结束了
时好时坏的原因就看网络情况,接口请求快就能拿到依赖的数据,慢就没数据。
正确写法是等待内部所有异步结束,或返回其执行结果,否则默认返回值是 Promise.resolve(undefined)
async function rightCode() { return request('/api') } async function rightCode() { await request('/api') }
这个问题一个项目里处理了好几次,不能说每次都粗心吧,写的人真就是没认真学习!
7 非预期的执行顺序
场景:有个接口返回结果常规的调用接口,返回结果为空时,根据 ok 值真假取不同的兜底值
const { result, ok } = await fetchData() const data = result || ok ? obj1 : obj2
如果你看不出问题在哪里,咱们看一下下面的运行结果是什么?
// 期望返回 1 const value1 = 1 || 2 ? 3 : 4 // 实际返回 3
💩 问题:不一样的原因是 逻辑或 || 运算符优先级高于 三元表达式
这种时候就建议不熟悉同学优先用() 处理执行的代码块
const data = result || (ok ? obj1 : obj2)
8 v-for 不熟练
① 移除列表元素
<script setup> function handleDelete(item) { list1.splice(list1.findIndex(item => item.id === id), 1) } </script> <template> <ul> <li v-for="item in list1" :key="item.id" @click="handleDelete(item)"> {{ item.name }} </li> </ul> </template>
② 列表渲染添加默认 key
<script setup> import { onMounted } from 'vue' onMounted(async () => { const data = await getData() for (let i = 0; i < data.length; i++) { list2.push({ ...data[i], id: i }) } }) </script> <template> <ul> <li v-for="item in list2" :key="item.id"> {{ item.name }} </li> </ul> </template>
💩 问题:v-for 遍历数组本身提供了下标 idx。
<template> <ul> <li v-for="(item, idx) in list2" :key="idx" @click="handleDelete(idx)"> {{ item.name }} </li> </ul> </template>
功能没问题,但是不优雅,知识学习没到位。

9 冗余重复代码
① 重复的字符串拼接
const baseURL = { dev: location.protocol + '//' + 'domain1', test: location.protocol + '//' + 'domain2', st: location.protocol + '//' + 'domain3', prod: location.protocol + '//' + 'domain4', mock: location.protocol + '//' + 'domain5', } const host = baseURL[env]
💩 问题:书写有些冗余,有简化空间
const baseURL = { dev: 'domain1', test: 'domain2', st: 'domain3', prod: 'domain4', mock: 'domain5', } const host = `${location.protocol}//${baseURL[env]}`
最佳做法 还是通过构建工具注入,这样避免代码中存在其它环境的值。
const host = `${location.protocol}//${process.env.VUE_APP_DOMAIN}`
为什么拉出来吐槽,因为这种代码是在单组件仓库中出现的,C端场景当页面引入几十个组件时候,就多了很多重复代码。
② CSS 冗余写法
.box { margin-top: 10px; margin-bottom: 10px; margin-left: 10px; margin-right: 10px; }
💩 问题:能简写的属性没有简写
.box { margin: 10px; }
推荐常用属性能简写的简写。
10 手搓 getQuery
先看看“手搓”的代码。
function getQuery() { const query = {} const href = window.location.href const searchArray = href.split('?')[1].split('&') searchArray.forEach(item => { const [key, value] = item.split('=') query[key] = value }) return query }
💩 问题:只考虑了有 Query 的情况,没有 Query 的时候调用就报错了
href.split('?')[1].split // Cannot read properties of undefined (reading 'split')
当然也有可优化点,location 提供了 search 属性可直接使用
function getQuery() { const search = location.search if(!search){ return {} } return search .slice(1) .split('&') .reduce((acc, cur) => { const [key, value] = cur.split('=') acc[key] = value return acc }, {}) }
事实上内置的 URLSearchParams 对象,可以直接解析 Query。
function getQuery() { const searchParams = new URLSearchParams(location.search) return Object.fromEntries( searchParams.entries() ) }
兼容性允许的情况下,优先推荐使用内置对象或方法实现功能
屏幕看太久,眼睛容易累?下面分享一款 NB 的外设。
编码“物理外挂”
别急着划走🤭,后面还有内容 🙏🏻!
近期深度使用了一款 「程序员专用」 显示器 明基RD280U,分享几个我觉得很棒的地方!

IDE 编码显示优化
你能想象一个显示器居然针对编码的IDE,提供了专门的显示优化!
轻触 logo 即可切换 深色/亮色 模式。

对比一下其它显示器的显示效果!
| 三星S32A600N | 明基RD280U |
|---|---|
![]() |
![]() |
可以比较明显的看出后者在优化后展示效果更棒,同时屏幕抗环境灯光能力也更强,前者在室内过亮时有些许泛白反光。
使用下来也确实眼睛看着更加的舒服👍🏻!
超Nice的夜间模式
自带背光灯,同时屏幕亮度等显示效果都能随环境光线强弱进行自动调节适应!
关闭室内所有灯光,效果如下
| 显示效果 | 背光灯 |
|---|---|
![]() |
![]() |
明基RD280U在无环境光的时候,使用体验也非常不错👍🏻,眼睛不会有不适,屏幕不泛白。
休息提醒
| 设置 |
|---|
![]() |
显示器自带的定时休息提示(屏幕右下角弹窗),👨🏻💻 日常大部分时间都在坐着,用这个提醒喝水&上厕所&站一站再好不过!
比安装各种提醒软件省事多了!
配套软件特色功能
显示器有一个配套的软件,除了完成硬件配置的功能外,还有一些增强!
| 配置窗口 | 桌面分区 | 自动任务&切换 |
|---|---|---|
![]() |
![]() |
![]() |
① 桌面分区
这个比较赞,Mac 系统本身应用分区功能比较弱,需要靠软件补齐这部分能力。
拖动窗口就能自动在鼠标唤起,选择目标分区,窗口就会自动贴合。

② 自动任务
可以设定不同的自动任务流程,不同的模式一键打开设定的一系列应用,同时屏幕调成对应的预设状态。

这个功能也是很 nice,一键切换工作/娱乐模式。关机的时候也可不用选择保留关机前的应用了。
接下来咱继续“品鉴代码”
11 冗长的取值判断
场景:深层次嵌套,变量名很长的取值判断。
function isOk(){ return ( testData && testData.helloResult && testData.helloResult.infoDetail && testData.helloResult.infoDetail.type === 'test' && testData.helloResult.infoDetail.status === 'ok' ) }
💩 问题:又臭又长的判断,看着比较难受
使用可选链后
function isOk(){ return ( testData?.helloResult?.infoDetail?.type === 'test' && testData?.helloResult?.infoDetail?.status === 'ok' ) }
与时俱进的了解新语法还是很有必要
12 莫名其妙的转换
直接看代码
判断有值的时候才做进一步处理
const value = await fetchData() if(value && String(value)){ // 进一步处理 }
💩 问题:再加 String 转换后判断,不是画蛇添足吗?
屏幕前的各位有遇到过没 🤦🏻♀️
13 处处 for 循环
① 数量不够时补全数据
const data = await fetchData() const count = Math.max(data.length, MIN_COUNT) for (let i = 0; i < count; i++) { if(!data[i]){ data[i] = { id: randomId(), otherKey: 'default' } } }
💩 问题:不管数量够不够都会存在无意义的遍历!
const data = await fetchData() const addedCount = MIN_COUNT - data.length for(let i = 0; i < addedCount; i++){ data.push({ id: randomId(), otherKey: 'default' }) }
当然也可以使用更优雅的 Array.from 方法生成新数组的过程中完成数据填充
const data = await fetchData() if(data.length < MIN_COUNT){ const addedData = Array.from({length: MIN_COUNT - data.length}, () => ({ id: randomId(), otherKey: 'default' })) data.push(...addedData) }
② 根据条件过滤数据
const data = await fetchData() const result = [] for (let i = 0; i < data.length; i++) { if(data[i].value>xx){ result.push(data[i]) } }
💩 问题:过滤场景优先推荐使用 filter 方法
const data = await fetchData() const result = data.filter(item => item.value>xx)
③ 增加数据字段
const data = await fetchData() for (let i = 0; i < data.length; i++) { data[i].newField = getNewField(data[i]) }
💩 问题:这种场景建议使用 map 方法,也不会影响原数组
const data = await fetchData() const newData = data.map(item => ({ ...item, newField: getNewField(item) }))
有些人真只会 for 基础循环一把羧!
最后
真心喜欢写代码的只占少数,草台班子过多🤦🏻♀️!
