ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

JavaScript单行代码技巧:覆盖数字、数组、对象与浏览器交互的20个实战速查

JavaScript单行代码技巧:覆盖数字、数组、对象与浏览器交互的20个实战速查 我写JavaScript这么些年最烦看到的就是一行能搞定的事非要用五六行去绕。尤其现在接口返回的数据结构越来越怪前端要做的格式化、判空、转换越来越多代码里堆的净是些重复劳动。今天这篇是我从日常项目里一条一条攒出来的单行代码笔记一共20条覆盖数字处理、数组操作、对象与类型判断、浏览器交互这几类最高频的场景。目标是让你在写业务的时候能一行说清楚的事绝对不用三行去磨叽。适合刚入门前端、想提升编码效率的朋友也适合写了好几年但还在用老旧写法折腾的老手。来直接上干货。1. 数字与字符串五个高频单行写法这一组代码我几乎每天都用。说白了业务代码里最多的工作就是“接口给我一个字符串我要把它变成数字接口给我一个浮点数我要把它格式化后展示”。这类操作本身不复杂但写法选不对后面全是坑。1.1 字符串转数字别掉进隐式转换的坑先说最基础的字符串转数字。我知道很多人写的是parseInt(str)但parseInt只解析整数部分遇到12.5px这种带单位的值会返回12遇到abc123这种开头不是数字的字符串直接返回NaN。很多时候这确实是我们想要的但它并不是真正意义上的“转数字”。我自己的习惯是优先用一元加运算符const num 3.14; // 3.14 const total 100; // 100 const zero ; // 0注意这里有个很多人忽略的细节的结果是0不是NaN。这是因为空字符串被强制转成数字时先变成0再参与运算。如果你在写一个“用户输入了空值就默认按0处理”的逻辑这个特性反而方便但如果你的本意是“空值算非法输入”就得先判断str 否则会被 0 悄悄带偏。Number(str)和str的结果完全一致两者没有本质区别纯看个人审美。我个人偏向用因为更短而且在处理表格里的纯数字单元格时.map(Number)这种写法特别清爽。真正要避开的坑是隐式转数字的其他路径比如str - 0、str * 1也能达到同样效果但读代码的人一眼看不出你的意图很容易误导后来维护的人。1.2 取整、保留小数、随机ID与日期格式化取整这块很多人第一反应是Math.floor但在单行场景里~~num是更短的写法const intVal ~~3.99; // 3 const negInt ~~-3.99; // -3注意是向上取整方向 const floorVal Math.trunc(3.99); // 3可读性更好~~的本质是“先转成32位整数再取反”所以它只适用于绝对值在2^31 - 1以内的数字。超过这个范围会得到错误结果。另一个隐藏差异是Math.floor(-3.99)是-4而~~(-3.99)是-3因为后者实际上是Math.trunc的行为。如果你要的是“向下取整”请老老实实写Math.floor别用~~硬凑。什么时候果断用~~就是性别下拉框、分页页码、步进器这类“明确知道数字在安全范围内且只是想去掉小数”的场景。保留两位小数又是一个重灾区。toFixed(2)看着方便但有个著名的浮点坑const bad (1.005).toFixed(2); // 1.00不是1.01 const good Math.round(1.005 * 100) / 100; // 1.01浮点数在二进制里没法精确表示1.005这样的十进制小数所以toFixed底层舍入时经常给出“反直觉”的结果。但你说toFixed完全不能用也不是。如果你只是要把价格展示给用户并且允许一点点误差toFixed(2)又短又省事。认真的财务计算必须走Math.round或者干脆用整数分做单位。顺便提一句toFixed返回的是字符串想让结果保持数字类型前面套一个即可num.toFixed(2)。随机ID和日期格式化也是高频痛点const randomId Math.random().toString(36).slice(2); // 一段随机字符串 const dateStr new Date().toISOString().slice(0, 10); // YYYY-MM-DDtoString(36)把数字转成36进制Math.random()产生的小数位数足够多拼出来就是一段大小写字母加数字的短ID。注意它适合做临时key、列表项ID、日志追踪但不适合做真正的唯一主键因为只是伪随机。crypto.randomUUID()才是标准做法一行代码也够用但要注意兼容性。toISOString().slice(0, 10)这个更常用它直接拿UTC时间如果在中国时区且只要日期那没问题需要本地日期就别用这个换成toLocaleDateString(zh-CN)更稳。2. 数组处理五个核心单行解法数组操作是前端业务里占用时间最多的部分。从后端接口拿到数组以后去重、排序、过滤、取值这些场景每个项目都要来一遍。这一节我挑的五个单行技巧都是我自己实测替换掉原来一堆循环的写法。2.1 去重、打乱、扁平化一行代码背后的取舍数组去重我见过最简洁也最不容易出错的写法就是配合Setconst unique [...new Set([1, 2, 2, 3, 3, 4])]; // [1, 2, 3, 4]Set本身就不允许重复值所以把数组丢进Set再展开回来重复项就没了。但这里有个边界Set去重用的是SameValueZero比较逻辑对原始类型很好用对对象数组就不行了。两个内容相同的对象因为引用地址不同会被当成两个不同的元素保留下来。所以这段代码只适合字符串、数字、布尔值这类基本类型组成的数组。对象数组要去重得改用Map配合某个唯一字段做 key那就不只是一行的事了我一般会抽成工具函数。随机打乱数组有段经典一行代码const shuffled arr.sort(() Math.random() - 0.5);原理很简单sort的排序函数返回正数就交换位置返回负数就保持不动而Math.random() - 0.5正负各一半概率于是顺序被搅乱。说句实在话这个写法只是勉强够用它产生的排列不是严格均匀分布的而且sort会改变原数组。如果只是做个“随机抽一道题”“随机展示几条推荐”这种低频场景完全没有问题但你要是写抽奖算法、AB测试分组别用这个。那种场景老老实实写Fisher-Yates洗牌哪怕多几行也是一行顶不上的。数组扁平化原生API一行就够const flatArr [[1, 2], [3, [4, 5]]].flat(Infinity); // [1, 2, 3, 4, 5]注意flat()默认只展开一层[3, [4, 5]]这种嵌套两层的数据默认展开完是[3, [4, 5]]还得再展开一次。想彻底扁平就传Infinity。代码越短越要留意边界如果你确定所有数据最多嵌套两层传2就够了性能也比Infinity好。还有就是flat会跳过数组里的空位比如[1, , 2].flat()会得到[1, 2]这个特性有时候能帮你清理脏数据有时候又会让你找半天bug。2.2 求最大最小值、过滤假值与快速交换取数组里最大/最小值大家肯定写过循环。现在一行就行const max Math.max(...[3, 9, 2, 8]); // 9 const min Math.min(...[3, 9, 2, 8]); // 2Math.max、Math.min接收的是一个个独立的数字参数不是数组所以要用展开运算符把数组拆开传进去。这个写法的风险也很明显数组太大展开成参数列表会爆栈。普通业务数组几十上百项都没事但如果你处理的是几万条数据的分组统计继续用这个就会卡顿甚至报错。那种场景我用Math.max(...arr)之前会先确认数组长度不超过万级否则就换reduce或者排序取值。过滤掉数组里的所有假值比如false、0、null、undefined、空字符串很多人会写一长串 filter 条件其实这么写就完了const cleanArr [0, 1, , a, false, null, undefined, NaN].filter(Boolean); // [1, a]filter(Boolean)的意思是“把每个元素扔进Boolean()里转一下为真的留下”。不用写Boolean(item)直接把Boolean构造函数作为回调传进去就行。不过要小心这个过滤是“宁可错杀不放过”0和空字符串都会没如果业务里0是有意义的就不能用这个一刀切。交换两个变量值解构是零成本写法let a 1, b 2; [a, b] [b, a];它没有引入任何中间变量代码一看就懂。实际项目里我反而不会刻意用这个因为大部分人读代码时对“左值右值”这种方式还是需要反应一下的。它很适合写算法题或者做笔记时用团队协作时如果旁边同事是新手我还是建议拆成两行加个临时变量。单行代码的价值是提高效率不是表演行为艺术。3. 对象、类型与逻辑控制五个少写三行的思路这一节的重点是判断和取值。在前端项目里最磨人的不是复杂的业务算法而是“这个值到底是不是我想要的类型”“这个对象到底是不是空的”“这个嵌套属性到底存不存在”。这些地方用对单行写法能少写一堆if。3.1 类型判断与空对象判断别再用 判断数据类型是 JavaScript 里最容易混淆的一块。typeof只能分清string、number、boolean、function、object这些大类遇到null、数组、日期、正则全都会返回object。所以遇到更细的类型判断我统一用这个写法const type Object.prototype.toString.call([]); // [object Array] const type2 Object.prototype.toString.call(null); // [object Null]原理是Object.prototype.toString会读取对象内部的Symbol.toStringTag属性返回统一的[object Xxx]格式。判断具体类型时拿它和字符串做比较就行const isArray Object.prototype.toString.call(arr) [object Array];有人会问Array.isArray(arr)不是更短吗没错判断数组用Array.isArray是标准答案。但Object.prototype.toString.call的覆盖面更广能区分Date、RegExp、Map、Set、Error这些内置对象。我通常把它封装成一个getType函数项目里所有类型判断都走它避免每个人各写一套。判断一个对象是不是空对象最简单的方式是数它的可枚举键const isEmpty Object.keys(obj).length 0;Object.keys只收集对象自身可枚举的属性不包含继承来的属性。所以{}会得到空数组{a: 1}得到[a]。边界情况要记住Date、Map这些内置对象用Object.keys默认拿到空数组会被误判成“空对象”。判断Map空不空应该用map.size 0判断Set空不空同理。所以这个一行技巧也是要看使用场景的处理普通对象字面量最合适。3.2 空值合并、可选链与布尔转换让分支代码变短以前我们给默认值都写a || b但||有个隐藏问题它会把0、、false这些“假值”也当成没值从而错误地启用默认值。比如用户填了0你本来是想存年龄0的结果直接给覆盖成默认值18了。空值合并运算符才是专门干这个活的const age input ?? 18; // 只有null/undefined才走默认值??只在左边是null或undefined时才会取右边的值0、、false都会原样保留。这是我强烈建议写代码时养成的习惯凡是“用户输入可能为假值但本身有意义”的场景一律用??而不是||。访问嵌套对象里的深层属性是另一个天天碰到的痛点。以前写res.data.list[0].name经常被Cannot read properties of undefined拍脸。现在有了可选链const userName res?.data?.list?.[0]?.name ?? 未知用户;一行同时做了三件事防止中间任何一层是null或undefined导致报错、安全取到最内层的name、拿不到时给一个兜底值。这里要注意的是可选链只管“读取路径上有没有值”它不会帮你判断值是不是空的。比如res.data存在但是空对象那访问.list得到的还是undefined并不会因为写了?.就跳过属性不存在带来的结果。把任意值转成布尔值最经典的写法是双感叹号const flag !!someValue; // true/false!翻转一次取反再翻转一次就是布尔类型了。这行代码最常见的使用场景是接口返回的数字0/1要变成布尔值、或者判断用户有没有头像然后决定要不要渲染占位图。细心的读者可能发现Boolean(someValue)和!!someValue效果一致但后者写起来更短而且一眼就能看出是在做布尔转换。我唯一要提醒的是别在复杂表达式里连续用多个!!比如!!(a !b)读起来太绕不如拆成变量。4. 浏览器与工程实践五个随手能用的单行API浏览器环境里藏着很多冷门又好用的 API尤其是 URL 处理、剪贴板、页面滚动这些场景原生一行就能解决根本不需要引工具库。我写完这一节以后最深的感受就是很多功能其实浏览器早给你做完了只是你不知道而已。4.1 剪贴板、页面滚动与URL参数读取以前实现“复制一段文本到剪贴板”要么用document.execCommand(copy)配合隐藏选区要么引入一个剪贴板库。现在原生一行就能搞定navigator.clipboard.writeText(要复制的文本).catch(() {});但我要提醒你navigator.clipboard只在https或localhost环境下才可用线上部署如果还是裸http接口会直接报权限错误。另外调用它时浏览器会弹出授权提示如果用户拒绝Promise 就会reject。所以这行代码后面最好跟着catch哪怕是一个空函数也比直接抛一条未捕获的 Promise 异常强。旧项目如果还在考虑老浏览器兼容我的一般做法是优先navigator.clipboard不可用再退回到execCommand方案但备份方案就不属于单行代码范畴了。页面滚动到顶部现在也有了原生平滑滚动window.scrollTo({ top: 0, behavior: smooth });注意scrollTo接收的不是两个数字参数而是一个配置对象。behavior: smooth实现平滑滚动不加就瞬间跳过去体验天差地别。同理滚动到某个元素的位置可以用el.scrollIntoView({ behavior: smooth })做“顶部导航点击后定位到锚点”这类交互非常方便。这里有个细节window.scrollTo处理的是整个窗口滚动条而某个内层容器里出现自己的滚动条时要用container.scrollTo而不是window.scrollTo我第一次在这个上面犯过错排查了好久。从URL里读取查询参数新手通常写正则匹配其实一行原生API就解决了const search new URLSearchParams(location.search); const id search.get(id);URLSearchParams会把location.search里的?a1b2解析成一个可查询的对象search.get(id)直接拿到对应参数。这个方法能自动处理中文和特殊字符的编码解码比手动写正则靠谱得多。还有两个同系方法也顺带说一下search.has(id)可以判断参数是否存在search.getAll(tag)可以获取同一个关键词的多个值这三个配合起来足够覆盖绝大多数取值场景。4.2 工程化边界为什么我不让你在业务代码里堆一行流前面讲的都是“怎么把代码写短”但我在实际带项目的时候反而会特地保留一些“笨写法”。原因很现实单行代码的最大问题不是它能不能运行而是它牺牲了多少可读性。比如解构交换变量[a, b] [b, a]技术上是干净的但团队里如果有新人看到这行代码第一反应是“这是在干嘛”你解释一遍他还半信半疑。类似~~num、str这种依赖隐式转换的写法更是如此。所以我给自己定了一个原则单行代码用于自己的脚本、工具函数、临时逻辑但进到多人协作的业务代码时优先保证别人不查文档也能看懂。如果一个单行技巧能配合注释使用那就完美了。比如const price input; // 转为数字一行代码加个简短注释保留效率的同时也保住了可维护性。另外说句公道话有些单行写法在性能上没有优势甚至更差。比如大数组的Math.max(...arr)展开运算的时间和内存开销都高于reduce。单行技巧只是给了你一个更紧凑的表达方式并不代表它一定是最优解。真正熟练的人是在知道所有方案优缺点之后再选一个适合当下的写法而不是看到哪行短就抄哪行。5. 20个单行技巧速查表写代码查资料的时候最怕的就是翻半天博客找到一篇干货结果还得自己一条条整理。这里我把前面提到的技巧汇总成一张速查表你直接拿走放到收藏夹里。表格里的每一行都是从日常高频场景里提炼出来的按类别分好后面写代码想不起来时回来扫一眼就行。分类技巧写法用途说明数字str字符串转数字空串会得到0数字~~num快速取整限32位整数范围数字Math.round(num * 100) / 100保留两位小数并修正浮点误差字符串Math.random().toString(36).slice(2)生成临时随机短ID字符串new Date().toISOString().slice(0, 10)获取格式化的YYYY-MM-DD数组[...new Set(arr)]数组去重适用于基本类型数组arr.sort(() Math.random() - 0.5)轻度随机打乱数组arr.flat(Infinity)多维数组彻底扁平化数组Math.max(...arr)/Math.min(...arr)求最大最小值注意大数组爆栈数组arr.filter(Boolean)过滤掉所有假值数组[a, b] [b, a]交换两个变量数组new Array(n).fill(0)初始化指定长度的数组数组arr.reduce((a, b) a b, 0) / arr.length计算数组平均值对象Object.keys(obj).length 0判断普通对象是否为空对象Object.fromEntries(arr)把键值对数组转成对象类型Object.prototype.toString.call(arg)精确判断内置类型逻辑a?.b?.c ?? 默认值安全取值并给默认值逻辑!!value任意值转布尔浏览器new URLSearchParams(location.search).get(id)读取URL查询参数浏览器navigator.clipboard.writeText(text)写入剪贴板注意HTTPS限制浏览器window.scrollTo({ top: 0, behavior: smooth })平滑滚动到顶部如果你细致的话会发现表格里其实有21行因为a?.b?.c ?? 默认值这个技巧拆开是两个能力我经常组合使用就合并成一行写了。你实际整理时也可以按自己的习惯拆或者合核心是记住这两个运算符的精确定位。最后再分享一点个人体会。我自己用下来使用频率最高的其实还不是这些最花哨的写法而是filter(Boolean)、??、?.、URLSearchParams这四样因为它们能直接砍掉以前一大片判空逻辑。单行代码这东西你掌握得越多写代码的速度和自信都会明显不一样但别走火入魔。有时候一行代码读起来太费劲拆成三行写反而效率更高。把这些技巧当工具放进工具箱需要的时候准确拿出来用而不是为了秀技巧强行套进每一段代码里。这样项目既清爽人也轻松。
返回列表