<?xml version="1.0" encoding="utf-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><title>Dark零点博客</title><link>https://www.tenca.cn/</link><description>记录小众热爱与生活碎片</description><item><title>js push/pop数组操作</title><link>https://www.tenca.cn/post/javascript-tutorial/7659.html</link><description>&lt;h1&gt;数组操作的“隐形推手”：吃透 JS 的 push 与 pop，避开 80% 的常见坑&lt;/h1&gt;
&lt;p&gt;写前端代码时，几乎所有人都在跟数组打交道。购物车加购、待办清单、甚至处理异步任务队列，底层往往都是最基础的增删动作。很多人习惯一遇到问题就搜 &lt;code&gt;js 数组添加元素&lt;/code&gt;，跳出的教程里，&lt;code&gt;push&lt;/code&gt; 和 &lt;code&gt;pop&lt;/code&gt; 常常被排在下半场，显得平平无奇。可真遇到需要实时维护数据流、或框架状态频繁刷新的场景，这两个方法往往是兜底的主力。它们不炫技，但摸清脾气后，用起来像瑞士军刀一样顺手。&lt;/p&gt;
&lt;p&gt;别只看它们的函数签名。&lt;code&gt;push&lt;/code&gt; 往末尾塞元素，&lt;strong&gt;返回值是新数组的长度，而非新数组本身&lt;/strong&gt;。不少新手在这里踩过坑，误把长度当数组去遍历渲染，直接导致页面空白或报错。&lt;code&gt;pop&lt;/code&gt; 的逻辑正好镜像，它把末尾元素抽走，&lt;strong&gt;返回值是被剔除的具体项，若原数组为空则返回 undefined&lt;/strong&gt;。清楚这两个返回值走向，能省去大量排查类型错误的精力。&lt;/p&gt;
&lt;p&gt;把它们当作后进先出（LIFO）的栈来调度，代码结构会清爽很多。实现命令撤销、操作历史回溯、或者分批处理文件上传时，&lt;code&gt;push&lt;/code&gt; 负责压入栈顶，&lt;code&gt;pop&lt;/code&gt; 负责弹出执行。比起手动掐头去尾调 &lt;code&gt;splice&lt;/code&gt;，栈模式的意图更直白，调试时也更容易打断点跟踪。若是需要做滚动缓存或限流窗口，让 &lt;code&gt;pop&lt;/code&gt; 配合计数逻辑自动清理旧数据，比维护一堆索引变量靠谱得多。&lt;/p&gt;
&lt;p&gt;现代框架的响应式机制对原地修改极其敏感。直接在 Vue 的 reactive 对象或 React 的 useState 数组里调 &lt;code&gt;push&lt;/code&gt;，视图有时会拒绝更新。这不是方法失效，而是框架没捕获到引用变化。&lt;strong&gt;涉及不可变数据流时，改用 [...arr, newItem] 重新赋值，或在框架特定 API 中显式触发依赖通知&lt;/strong&gt;。分清原生方法与上层架构的边界，比纠结语法本身更重要。&lt;/p&gt;
&lt;p&gt;性能层面不必有负担。&lt;code&gt;push&lt;/code&gt; 和 &lt;code&gt;pop&lt;/code&gt; 的时间复杂度稳定在 O(1)，它们是数组操作里耗时最短的梯队，完胜 &lt;code&gt;unshift&lt;/code&gt;、&lt;code&gt;shift&lt;/code&gt; 或 &lt;code&gt;slice&lt;/code&gt;。只有当数组规模突破十万级且高频进行头尾操作时，底层可能触发内存重分配。此时可预先设定预估容量，或考虑换用环形缓冲区。常规业务完全不需要过度优化，保持代码可读性才是正解。&lt;/p&gt;
&lt;p&gt;实战中守住这几条操作纪律：
&lt;strong&gt;pop 之前务必判断 arr.length &amp;gt; 0，杜绝 undefined 参与后续计算&lt;/strong&gt;
&lt;strong&gt;一次 push 多个参数直接用逗号分隔，不要额外包一层 Array&lt;/strong&gt;
&lt;strong&gt;纯展示型静态列表用 concat 或展开符生成新数组，交互型动态队列优先 push/pop&lt;/strong&gt;
&lt;strong&gt;涉及多线程 Web Worker 传递大数组时，用 structuredClone 或序列化规避引用污染&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;数组工具箱里，亮度最高的往往不是带装饰的新语法糖。&lt;code&gt;push&lt;/code&gt; 和 &lt;code&gt;pop&lt;/code&gt; 就像机械键盘里的标准键帽，结构干脆、触发明确。知道何时让它们进场、何时让路给不可变方案，代码自然会少许多冗余的防御姿势。下次再面对不断滚动的数据流，不妨先用栈思维理顺脉络，顺手关掉多余的 console.log，看逻辑自己跑通。&lt;/p&gt;</description><pubDate>Sun, 24 May 2026 00:00:34 +0800</pubDate></item><item><title>js shift/unshift数组</title><link>https://www.tenca.cn/post/javascript-tutorial/7658.html</link><description>&lt;h1&gt;JS数组头部操作：shift与unshift的隐藏代价与实用场景&lt;/h1&gt;
&lt;p&gt;写前端代码时，很多人习惯用 &lt;code&gt;push&lt;/code&gt; 和 &lt;code&gt;pop&lt;/code&gt; 处理数据流。一旦遇到需要往数组开头塞数据或剔除旧记录的場景，&lt;code&gt;unshift&lt;/code&gt; 和 &lt;code&gt;shift&lt;/code&gt; 往往成为第一反应。这两个方法语法极短，但实际踩坑频率并不低。它们到底在底层完成了什么动作？日常开发中该怎么用才不拖累渲染？&lt;/p&gt;
&lt;p&gt;直接看运行结果：&lt;strong&gt;&lt;code&gt;shift()&lt;/code&gt; 会移除数组的第一个元素，并返回该元素的值；&lt;code&gt;unshift()&lt;/code&gt; 会在数组最前方插入一个或多个新元素，同时返回新数组的长度。&lt;/strong&gt; 两者都会直接修改原数组，不产生副本。这意味着只要存在对该数组的引用，后续所有相关变量都会同步变化。提前意识到“就地修改”的特性，能拦截大半因隐式数据污染引发的联级 Bug。&lt;/p&gt;
&lt;p&gt;别被简洁的 API 误导。JavaScript 数组在底层通常映射为连续存储结构，&lt;strong&gt;头部插入或删除元素，会强制引擎将剩余所有元素的索引重新计算并前移或后移&lt;/strong&gt;。若你维护着一个包含五万条日志的任务队列，每次轮询都执行一次 &lt;code&gt;unshift&lt;/code&gt;，V8 引擎必须逐一遍历并完成指针重绑。时间复杂度稳定在 O(n)，在高频交互或长列表场景中，这种隐形位移足以拉低主线程吞吐量。多数开发者只关注返回值，却忽略了数据规模才是性能分界线。&lt;/p&gt;
&lt;p&gt;遇到实时消息推送、聊天记录滚动或任务缓冲池时，&lt;code&gt;shift&lt;/code&gt; 配合 &lt;code&gt;pop&lt;/code&gt; 天然契合先进先出（FIFO）模型。设定固定容量上限，新增数据用 &lt;code&gt;push&lt;/code&gt; 入队，溢出时直接调用 &lt;code&gt;shift()&lt;/code&gt; 挤出陈旧条目。代码意图清晰，内存占用可控。此时它不是负担，而是精准的语义化工具。&lt;/p&gt;
&lt;p&gt;如果业务确实需要频繁向头部堆积动态内容，与其硬扛 &lt;code&gt;unshift&lt;/code&gt; 的平移成本，不如调整数据结构策略。可以先用普通数组承接增量，达到阈值后执行一次 &lt;strong&gt;&lt;code&gt;reverse()&lt;/code&gt; 结合 &lt;code&gt;concat()&lt;/code&gt;&lt;/strong&gt;，或直接改用对象键值对按时间戳倒排。当变更极度密集且对顺序敏感时，引入基于双端链表的 Deque 实现会更稳健。核心原则始终是：&lt;strong&gt;降低头部操作频次，将集中式挪动拆分为低耗费的尾部追加。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;另一种常见误区是为了解决任意位置增删，反复套用 &lt;code&gt;unshift&lt;/code&gt; 配合循环移位。实际上 &lt;strong&gt;&lt;code&gt;splice(startIndex, deleteCount, newItem...)&lt;/code&gt;&lt;/strong&gt; 才是原生更通用的路径。它支持单点或多点批量替换，内部走査逻辑经过多年迭代，应对复杂区间操作更从容。面对需要动态重组的业务流，优先评估 &lt;code&gt;splice&lt;/code&gt; 或不可变更新范式，往往比手动搬运索引少写三行样板代码。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;shift&lt;/code&gt; 和 &lt;code&gt;unshift&lt;/code&gt; 从来不是过气方法，只是使用边界需要划清。适合小规模头部调度、明确需要 FIFO 语义的场景；不适合高频触发、海量数据的粗暴操作。摸清它们背后的索引平移机制，搭配合理的替代方案，才能在保持代码可读性的同时守住运行效率。下次再碰到数组头部的需求，先快速过一遍两件事：当前数据量级是多少？单位时间内调用几次？答案通常会自己浮现。&lt;/p&gt;</description><pubDate>Sat, 23 May 2026 18:00:47 +0800</pubDate></item><item><title>js indexOf查找元素索引</title><link>https://www.tenca.cn/post/javascript-tutorial/7657.html</link><description>&lt;h1&gt;别只会写 &lt;code&gt;arr.indexOf(val)&lt;/code&gt;，这3个隐蔽细节决定你的代码会不会埋雷&lt;/h1&gt;
&lt;p&gt;写前端列表筛选时，很多人习惯顺手甩一句 &lt;code&gt;arr.indexOf(target)&lt;/code&gt;。看着清爽，跑起来却经常返回 &lt;code&gt;-1&lt;/code&gt; 或者漏掉本该命中的元素。方法本身没有毛病，真正的问题是很多人没摸清它的脾气。今天不抄语法文档，直接拆解几个高频踩坑现场和对应的破局思路。&lt;/p&gt;
&lt;p&gt;严格相等是一把双刃剑。&lt;code&gt;indexOf&lt;/code&gt; 底层走的是 &lt;code&gt;===&lt;/code&gt;，这意味着类型必须严丝合缝。数组里存的是数字 &lt;code&gt;10&lt;/code&gt;，你传进去字符串 &lt;code&gt;&quot;10&quot;&lt;/code&gt;，结果必然是未命中。日常对接接口时，这种“隐形类型差”特别常见。&lt;strong&gt;遇到跨类型匹配，不要硬往里塞，先用 &lt;code&gt;Number()&lt;/code&gt; 或模板字符串做一次显式清洗，或者把类型转换逻辑提到外层&lt;/strong&gt;。如果业务确实需要宽容匹配，自己写个带 &lt;code&gt;==&lt;/code&gt; 的循环反而更透明，别让底层方法替你猜意图。&lt;/p&gt;
&lt;p&gt;对象检索则是另一座大山。数据源里躺着一堆用户实体，你试图用 &lt;code&gt;indexOf({ id: 1, name: &quot;A&quot; })&lt;/code&gt; 定位目标，返回的永远是一串刺眼的 &lt;code&gt;-1&lt;/code&gt;。核心原因在于对象按引用比对，内存地址不同就等于不相等。&lt;strong&gt;这时候换执行路径比死磕原方法更高效&lt;/strong&gt;。现代项目已全面转向 ES6，直接用 &lt;code&gt;arr.findIndex(u =&amp;gt; u.id === targetId)&lt;/code&gt; 替换。不仅支持多字段组合判断，调试时也能精准断点。若受限于老工程无法升级，&lt;strong&gt;提前把对象池拍平成 &lt;code&gt;Map(id, index)&lt;/code&gt; 或 &lt;code&gt;Set(id)&lt;/code&gt;，后续查找时间复杂度直接压到 O(1)&lt;/strong&gt;，比反复遍历靠谱得多。&lt;/p&gt;
&lt;p&gt;还有个容易被忽视的边界值是 &lt;code&gt;NaN&lt;/code&gt;。无论数组里是否存在它，&lt;code&gt;indexOf(NaN)&lt;/code&gt; 都固定吐 &lt;code&gt;-1&lt;/code&gt;。这不是引擎抽风，而是 IEEE 754 规范的特性。排查到这里卡住时，说明场景可能已经超出了简单索引的范畴。&lt;strong&gt;快速对齐实际需求就能避开纠结&lt;/strong&gt;：只查单一原始值且接受 &lt;code&gt;-1&lt;/code&gt; 兜底，守 &lt;code&gt;indexOf&lt;/code&gt; 没问题；要拿第一个满足条件的项，切 &lt;code&gt;findIndex&lt;/code&gt;；只需知道“有没有”，直接上 &lt;code&gt;includes&lt;/code&gt;。工具越聚焦，代码越干净。&lt;/p&gt;
&lt;p&gt;熟悉一个 API 的底线，往往比罗列二十个参数更管用。&lt;code&gt;indexOf&lt;/code&gt; 依然适合处理类型明确、规模不大的原始值盘点，但面对复杂对象、动态条件或高频查询，及时切换思路能省下一大段调试时间。写代码就像整理档案柜，选对分类逻辑，下次翻阅就不会在一堆乱序里翻半天。看清边界，懂得适时放手，才是日常开发里最扎实的基本功。&lt;/p&gt;</description><pubDate>Sat, 23 May 2026 12:00:26 +0800</pubDate></item><item><title>js reverse反转数组</title><link>https://www.tenca.cn/post/javascript-tutorial/7656.html</link><description>&lt;h1&gt;《JS 数组反转不止 reverse()：日常开发中的高效写法与边界陷阱》&lt;/h1&gt;
&lt;p&gt;做前端数据处理时，偶尔会碰到需要把数组倒序输出的场景。比如最新评论置顶、时间轴倒排，或者把接口返回的正向列表翻转为页面渲染需要的顺序。很多人第一反应就是甩一句 &lt;code&gt;arr.reverse()&lt;/code&gt;，代码确实短，但顺手用久了，往往会在后续逻辑里埋下隐雷。数组反转这件事，看着简单，实际操作起来却有不少值得拆解的细节。&lt;/p&gt;
&lt;p&gt;原生提供的 &lt;code&gt;Array.prototype.reverse()&lt;/code&gt; 是个原地操作的方法。&lt;strong&gt;它会直接修改原始数组，并返回同一个引用&lt;/strong&gt;。这意味着如果你拿的是状态管理器里的数据，或者正在多处共用的列表，反转变量后原数据也会跟着“变脸”。React 或 Vue 的响应式机制最忌讳这种隐式突变，视图更新容易错乱。遇到这种情况，先冻结副本再反转才是稳妥做法。&lt;/p&gt;
&lt;p&gt;不想动原数据，可以用展开语法快速脱壳：&lt;strong&gt;&lt;code&gt;[...arr].reverse()&lt;/code&gt;&lt;/strong&gt;。这行代码底层会先克隆一份新数组，再在副本上执行倒序，返回值干净利落，且不会干扰原有依赖链。对于老项目兼容，&lt;code&gt;arr.slice().reverse()&lt;/code&gt; 同样能避开副作用。两者性能差距极小，现代引擎都能无缝优化，选哪个纯看团队规范。如果追求函数式风格，也可以配合 &lt;code&gt;toReversed()&lt;/code&gt;，这是 ES2023 新增的非变异方法，语义更直白，新版浏览器已原生支持。&lt;/p&gt;
&lt;p&gt;反转逻辑跑通了，还得留意数据规模带来的影响。普通对象数组在万级别以内，上述写法毫无压力。但如果处理的是百万级长列表，频繁创建临时副本会拉扯内存分配器。&lt;strong&gt;此时更适合用双指针原地交换&lt;/strong&gt;，手动控制左右索引相向移动并 Swap，避免 GC 频繁回收造成的页面卡顿。另外，&lt;code&gt;TypedArray&lt;/code&gt;（如 Uint8Array）不能直接套用标准数组方法，得走 &lt;code&gt;new Array(...typedArray)&lt;/code&gt; 转换后再处理，或直接操作底层缓冲区偏移量。不同数据结构该匹配什么手段，得分清再动手。&lt;/p&gt;
&lt;p&gt;实际业务里，数组很少孤立存在。通常要经历过滤、映射、排序，最后才轮到处决阶段。比如把用户反馈列表按优先级挑出后倒序展示，建议用链式调用串联步骤：&lt;strong&gt;先完成所有衍生计算，再统一执行反转操作&lt;/strong&gt;。这样既能保持状态流转清晰，又能避免中间过程反复拷贝数据。遇到需要稳定性的场景，甚至可以提前声明好反向映射表，用空间换时间，省去动态反转的开销。&lt;/p&gt;
&lt;p&gt;数组反转不是炫技题，而是工程化的基础动作。摸清 mutate 与 clone 的边界，清楚何时该偷懒用内置方法、何时该手写双指针，写出来的数据处理流才会既轻便又抗造。下次再碰到倒序需求，别急着敲 &lt;code&gt;reverse()&lt;/code&gt;，先看看手里的数据到底需不需要被“伤到”。&lt;/p&gt;</description><pubDate>Sat, 23 May 2026 06:00:26 +0800</pubDate></item><item><title>js join数组转字符串</title><link>https://www.tenca.cn/post/javascript-tutorial/7655.html</link><description>&lt;h1&gt;别再手动拼接了：JS中 &lt;code&gt;join()&lt;/code&gt; 的实战细节与常见陷阱&lt;/h1&gt;
&lt;p&gt;写前端代码时，你是不是也常遇到这种场景：接了一组标签、路径或表单字段，准备塞进模板或传给后端。很多人习惯写 &lt;code&gt;for&lt;/code&gt; 循环逐一把元素用 &lt;code&gt;+&lt;/code&gt; 拼起来，或者依赖递归累加。代码勉强能跑，但看着臃肿不说，后期改需求时还得反复核对边界条件。其实 JavaScript 早就备好了更利落的方案——&lt;code&gt;Array.prototype.join()&lt;/code&gt;。它能把零散的数组元素整齐地拧成一根字符串，但真要拿进生产环境，若不摸清类型转换规则和性能底线，照样会踩坑。&lt;/p&gt;
&lt;p&gt;调用的核心逻辑比表面看起来更克制。数组原型提供的 &lt;code&gt;join&lt;/code&gt; 接收一个可选的分隔符参数，不传时默认插入逗号。你只需在目标数组末尾挂上调用，引擎就会按顺序抽取元素完成缝合。比如处理一组分类名称，&lt;code&gt;['前端', '交互', '可视化'].join(' / ')&lt;/code&gt; 直接产出整洁的展示文案。这里最容易忽略的是它的&lt;strong&gt;隐式类型转换机制&lt;/strong&gt;：数字和布尔值会原样转为对应字面量，但一旦数组里混入对象或函数，引擎会调用其默认的 &lt;code&gt;toString&lt;/code&gt;，结果往往是 &lt;code&gt;[object Object]&lt;/code&gt;。面对结构化数据，&lt;strong&gt;不要在 &lt;code&gt;join&lt;/code&gt; 后补救，而在拼接前用 &lt;code&gt;map&lt;/code&gt; 提取必要字段&lt;/strong&gt;，让数据流保持纯净。&lt;/p&gt;
&lt;p&gt;实际业务中，&lt;code&gt;join&lt;/code&gt; 常常被拿来干脏活累活，但也正因如此，它藏了不少隐蔽的雷区。如果你在用它导 CSV 表格，数组里的 &lt;code&gt;null&lt;/code&gt; 或 &lt;code&gt;undefined&lt;/code&gt; 会被静默替换为空字符串，原本该有内容的单元格直接“消失”，导致下游解析时列数错位。应对思路很直接：&lt;strong&gt;提前统一填充占位符&lt;/strong&gt;，例如用 &lt;code&gt;(val !== undefined ? val : '-').join(',')&lt;/code&gt; 规避静默丢失。另一个常被忽视的点是分隔符本身。当分隔符包含反斜杠或类似正则元字符的符号时，传入某些模板引擎或 SQL 语句解析器可能引发转义冲突。养成在拼接完成后对敏感字符做一次基础过滤的习惯，能省去大量深夜排查的时间。&lt;/p&gt;
&lt;p&gt;遇到超长列表时，性能表现往往会露馅。当数组长度突破数万级，&lt;code&gt;join&lt;/code&gt; 会在内存中一次性构建完整的字符串副本，浏览器主线程容易被长时间占用，页面出现明显卡顿。此时不应盲目依赖原生方法，&lt;strong&gt;改为分批截取切片后逐个拼接，或直接走虚拟滚动/流式渲染&lt;/strong&gt;，才是稳住体验的正解。至于它和 &lt;code&gt;toString()&lt;/code&gt; 的取舍，两者底层都走 C++ 优化的字符拷贝，但 &lt;code&gt;join&lt;/code&gt; 胜在可定制间隔且语义更明确。微观 benchmark 里 &lt;code&gt;join&lt;/code&gt; 偶尔慢零点几毫秒，但在可读性层面，它能清晰传达“我要把独立元素排成一行”的意图。选工具的标准从来不是跑分，而是团队后续接手时能否一眼看懂你的设计初衷。&lt;/p&gt;
&lt;p&gt;数组转字符串看似只是几行代码的堆砌，却最能反映开发者对数据边界的把控力。熟练运用 &lt;code&gt;join&lt;/code&gt; 不需要背诵手册，而是建立一套前置判断的习惯：看清数据类型、预判空值走向、评估体量规模，再决定分隔符和清洗时机。把重复劳动交给引擎内置方法，把算力留给真正复杂的业务模型。下次再面对一堆待处理的数组元素时，顺手挂上一段清晰的 &lt;code&gt;.join('你的目标分隔符')&lt;/code&gt;，代码降级的同时，调试成本也跟着直线下降。&lt;/p&gt;</description><pubDate>Sat, 23 May 2026 00:00:31 +0800</pubDate></item><item><title>js split字符串转数组</title><link>https://www.tenca.cn/post/javascript-tutorial/7654.html</link><description>&lt;h1&gt;别只懂逗号分隔：JS split() 实战避坑指南&lt;/h1&gt;
&lt;p&gt;处理用户输入的标签流、解析后端返回的管道符字段，或是简单切割一行运行日志，&lt;strong&gt;&lt;code&gt;String.prototype.split()&lt;/code&gt; 几乎每天都在后台默默干活&lt;/strong&gt;。很多人默认它只是按符号一刀两断，真到排查线上偶发 Bug 时，才会发现空元素污染、意外超限或正则误伤让人抓狂。摸清它的底层脾气，能直接跳过一半的调试弯路。&lt;/p&gt;
&lt;p&gt;日常直接传单一分隔符最省事，但源数据若带着&lt;strong&gt;尾部残留符号&lt;/strong&gt;，切片后必然多出个空字符串。比如日志里常见的 &lt;code&gt;&quot;node,v16,production,&quot;&lt;/code&gt;，强行推给列表组件会导致最后一项渲染错位。与其事后遍历过滤，不如在源头统一规则：&lt;strong&gt;将连续相同分隔符合并为一次切割&lt;/strong&gt;。直接传入正则 &lt;code&gt;/[,]+/&lt;/code&gt; 即可抹平多余切口，数组长度回归预期，后续处理链条也能保持干净。&lt;/p&gt;
&lt;p&gt;切完数据接着常遇到的问题是如何快速瘦身。&lt;strong&gt;控制数组上限能显著压低内存开销&lt;/strong&gt;。&lt;code&gt;split()&lt;/code&gt; 原生支持第二个参数 &lt;code&gt;limit&lt;/code&gt;，限定返回的最大子串数量。遇到超长配置串 &lt;code&gt;&quot;key1=val1&amp;amp;key2=val2&amp;amp;key3=val3...&quot;&lt;/code&gt;，只需调用 &lt;code&gt;.split(&quot;&amp;amp;&quot;, 3)&lt;/code&gt; 就能拿到前三组核心键值对，剩余部分直接丢弃。注意这个参数仅在成功匹配到分隔符时才生效，若分隔符不存在或传入空字符串，引擎会忽略限制并返回完整数组，使用时务必对齐预期。&lt;/p&gt;
&lt;p&gt;复杂场景下，硬编码字符拼接极易遗漏边缘情况。建议&lt;strong&gt;将多分隔符逻辑收敛进正则表达式&lt;/strong&gt;。面对混排符号如 &lt;code&gt;&quot;user-id:1024 | role-admin [dev]&quot;&lt;/code&gt;，编写 &lt;code&gt;/[:|\[\]]+/&lt;/code&gt; 能一次性定位所有有效切口。这里有个隐形坑点：正则里的元字符（如 &lt;code&gt;. * + ? ^ $ \ |&lt;/code&gt;）必须转义，否则会被当作通配符吞噬正常字符，导致切片位置彻底偏移。若动态接收外部符号，先做一次安全包装再丢进数组，能避开运行时崩溃。&lt;/p&gt;
&lt;p&gt;实际开发中，拆分从来不是单步动作。&lt;strong&gt;切分前的清洗与切分后的类型校准要串联起来&lt;/strong&gt;。常规做法是 &lt;code&gt;.trim()&lt;/code&gt; 剥离首尾空白 → &lt;code&gt;split(规则)&lt;/code&gt; 执行切割 → &lt;code&gt;[...arr].map(Number || String)&lt;/code&gt; 统一映射。这套组合拳跑通了数据管道，既防抖又防错。工具方法的价值不在于语法多冷门，而在于能不能无缝嵌进你的数据处理流。下次再面对一坨原始文本，先理清切割边界，再选对应策略，代码自然轻省不少。&lt;/p&gt;</description><pubDate>Fri, 22 May 2026 18:00:27 +0800</pubDate></item><item><title>js substring字符串截取</title><link>https://www.tenca.cn/post/javascript-tutorial/7653.html</link><description>&lt;h1&gt;别再随手切开字符串了：JS substring 的实用姿势与隐藏细节&lt;/h1&gt;
&lt;p&gt;做数据渲染或者处理表单输入时，切字符串几乎是每天会碰到的基础操作。很多人条件反射就会去翻文档，或者直接套用最熟悉的 &lt;code&gt;slice&lt;/code&gt;。其实对于纯粹的整数索引截取，&lt;code&gt;substring&lt;/code&gt; 依然是一个被低估的工具。它不修改原对象，行为稳定，只是几个隐蔽的参数特性没摸透，很容易写出偏离预期的逻辑漏洞。&lt;/p&gt;
&lt;p&gt;理解它的运行轨迹，比死记硬背签名更有效。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;substring(start, end)&lt;/code&gt; 默认按左闭右开区间工作，即包含起始位，不包含结束位。实际开发中最常被误读的是它的&lt;strong&gt;内置规则覆盖机制&lt;/strong&gt;。当起始索引大于结束索引时，方法不会抛出异常，而是自动对调两者；若遇到负数，则强制转为 &lt;code&gt;0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这意味着一段看似跳过的代码，往往能平稳运行：
&lt;code&gt;const src = 'javascript'; console.log(src.substring(8, 2));&lt;/code&gt;
最终返回 &lt;code&gt;'vascri'&lt;/code&gt;。内部完成了隐式交换。这个特性在处理动态计算的起止点时，能省下不少 &lt;code&gt;Math.max/min&lt;/code&gt; 的判断。但反过来说，如果业务强依赖严格的方向顺序，这种“自作主张”的反转就成了隐形地雷，必须在调用前显式对齐大小。&lt;/p&gt;
&lt;p&gt;落到具体的页面需求里，掌握这几个高频用法就足够应对大部分场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;智能截断长文本&lt;/strong&gt;
列表组件展示摘要时，通常需要限定显示长度并在尾部补省略号。结合长度判断可以直接一行闭环：
&lt;strong&gt;&lt;code&gt;len &amp;gt; 24 ? str.substring(0, 24) + '…' : str&lt;/code&gt;&lt;/strong&gt;
无需额外兜底越界。当原始长度未达到阈值，第二个参数会被引擎自动降级为字符串全长，直接吐出完整内容。这种自带容错的特性，非常适合模板引擎或 UI 组件内的防溢出处理。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;锚点定位提取子串&lt;/strong&gt;
解析配置项、拆分文件名或剥离路由前缀，往往依赖固定分隔符。先抓取锚点位置，再以该点为起点往后拉取：
&lt;code&gt;const filePath = 'assets/img/logo.svg'; const slashPos = filePath.lastIndexOf('/'); const pureName = filePath.substring(slashPos + 1);&lt;/code&gt;
第二个参数省略代表直通末尾。相比正则表达式，这种纯数值偏移的方案执行路径更短，在高频刷新的列表或海量数据清洗管道里，能压住不必要的 GC 压力。&lt;/p&gt;
&lt;p&gt;既然生态里有功能更广的 &lt;code&gt;slice&lt;/code&gt; 和已淘汰的 &lt;code&gt;substr&lt;/code&gt;，为什么还要保留 &lt;code&gt;substring&lt;/code&gt; 的存在？核心差异在于&lt;strong&gt;心智成本的确定&lt;/strong&gt;。&lt;code&gt;slice&lt;/code&gt; 拥抱负数索引，灵活性高但容易让维护者反复核对倒数坐标；&lt;code&gt;substr&lt;/code&gt; 的第二参数代表长度而非终点，语义极易打架。&lt;code&gt;substring&lt;/code&gt; 始终坚守基于绝对位置的直观逻辑，代码意图一目了然。当不需要处理相对距离或步长遍历时，它就是最轻量的解法。&lt;/p&gt;
&lt;p&gt;稳妥使用的底线，永远落在入参净化上。尤其是拼接第三方数据或处理用户拖拽产生的动态区间时，建议抽离一层安全获取函数。把越界、非数字、反向区间等脏数据拦截在最外层，比在业务层疯狂堆砌守卫条件要干净得多。现代构建工具虽然推崇各种字符串辅助库，但原生 API 零引入、零兼容摩擦的特性，在小包体积或离线环境下依然是不可替代的基本盘。&lt;/p&gt;
&lt;p&gt;字符串操作从来不是炫技的舞台，稳定可预测才是交付的底气。摸清 &lt;code&gt;substring&lt;/code&gt; 的自动排序脾气，避开参数反转带来的逻辑偏差，把调试精力留给真正的交互难点，工程结构自然会轻盈不少。下次遇到单向固定区间的切割任务，直接交还给它，顺手还能砍掉好几行防御代码。&lt;/p&gt;</description><pubDate>Fri, 22 May 2026 12:00:32 +0800</pubDate></item><item><title>js substr截取字符串</title><link>https://www.tenca.cn/post/javascript-tutorial/7652.html</link><description>&lt;h1&gt;别再用 &lt;code&gt;substr&lt;/code&gt; 硬扛字符串了，这几点踩坑经验比语法更重要&lt;/h1&gt;
&lt;p&gt;写前端处理数据时，谁没遇过要把一段长文本“掐头去尾”的需求？手机号脱敏、身份证号掩码、路径切片……很多人下意识敲下的就是 &lt;code&gt;substr&lt;/code&gt;。这个方法确实老牌且顺手，但如果你只把它当成普通的截取工具，代码跑起来可能会突然爆出边界错误，或者在维护时被老同事吐槽“怎么还在用淘汰的方法”。字符串截取看着只是两行代码，里头藏着的参数逻辑、编码陷阱和演进路线，才是真正拉开工程质量的细节。&lt;/p&gt;
&lt;p&gt;记住它的核心签名：&lt;code&gt;str.substr(start, length)&lt;/code&gt;。第一个参数决定从哪个索引开始切，第二个参数控制你要拿多少个字。索引从 &lt;code&gt;0&lt;/code&gt; 算起，正数从头往后找，负数则直接从末尾倒推。比如 &lt;code&gt;&quot;2023-10-25&quot;.substr(-2)&lt;/code&gt; 能直接拿到 &lt;code&gt;&quot;25&quot;&lt;/code&gt;，省去了先分割再拼接的繁琐链路。但要注意，&lt;strong&gt;第二个参数的缺失或异常类型会直接改变执行轨迹&lt;/strong&gt;。不传该参数时，方法会一路截取到字符串末尾；若传入负数，规范会将其强制转为 &lt;code&gt;0&lt;/code&gt;，最终返回空字符串。很多隐蔽的线上问题，就藏在这些“默认行为”里。&lt;/p&gt;
&lt;p&gt;碰到复杂数据源，边界条件最容易翻车。当起始位置大于等于字符串总长度时，&lt;code&gt;substr&lt;/code&gt; 不会抛出异常，而是安静地返回一句空字符串。这种温和的容错在开发期挺好骗，一旦对接第三方脏数据，后续的 &lt;code&gt;.toUpperCase()&lt;/code&gt; 或数组解构就会连环崩溃。更麻烦的是多字节字符的编码错位。JavaScript 内部采用 UTF-16 存储，一个 Emoji 或生僻字占据两个代码单元。&lt;code&gt;substr&lt;/code&gt; 按纯数字索引硬切时，极大概率把半截代理对劈开，页面上直接渲染出 &lt;code&gt;` 乱码。这时候单纯调优索引已经无效，必须前置判断数据是否含有非 BMP 字符，必要时改用正则或&lt;/code&gt;Array.from` 分词后再切片。&lt;/p&gt;
&lt;p&gt;既然用得顺手，为什么官方文档早早给 &lt;code&gt;substr&lt;/code&gt; 贴上了“遗留 API”标签？底层引擎早已停止针对它的性能优化，标准委员会也更推荐语义清晰的替代方案。&lt;code&gt;slice&lt;/code&gt; 同样支持负数索引，且长度参数完全遵循直觉；&lt;code&gt;substring&lt;/code&gt; 则自带参数自动校正机制（如 &lt;code&gt;substring(5, 2)&lt;/code&gt; 会互换为 &lt;code&gt;substring(2, 5)&lt;/code&gt;），防呆能力更强。老项目若想逐步替换，&lt;strong&gt;切忌直接全局搜索替换&lt;/strong&gt;。正确的姿势是先定位所有调用点，逐行核对业务是否依赖“负数长度转全截”的历史惯性，确认无副作用后统一迁移至 &lt;code&gt;slice&lt;/code&gt;。迁移完成后顺手补上一组覆盖越界、空串和特殊字符的单元测试，能堵住 90% 的回归漏洞。&lt;/p&gt;
&lt;p&gt;抛开底层原理，日常提效靠的是场景化组合。固定格式的日志或配置项解析，别死磕正则表达式。提取版本号 &lt;code&gt;&quot;v2.3.1-build.4&quot;&lt;/code&gt; 的主版本段，直接 &lt;code&gt;&quot;v2.3.1-build.4&quot;.substr(1, 1)&lt;/code&gt; 比写完整匹配模式快得多，可读性也高出一个层级。后端压扁的时间戳 &lt;code&gt;&quot;20231025143022&quot;&lt;/code&gt; 需要还原成日期格式，链式拼合 &lt;code&gt;str.slice(0, 4) + &quot;-&quot; + str.slice(4, 6)&lt;/code&gt; 既避免中间变量泛滥，又让数据流向一目了然。把截取动作拆成独立步骤，后期排查链路会轻松很多。&lt;/p&gt;
&lt;p&gt;字符串操作是前端工程的毛细血管，量级不大却牵一发而动全身。&lt;code&gt;substr&lt;/code&gt; 承载过无数早期项目的稳定运行，但技术栈的演进从不为惯性买单。摸清它的脾气，看清它的边界，适时切换到更现代化的处理范式，才能在日复一日的字段清洗中保持代码整洁。下次准备敲下那行代码时，不妨停下来问自己一句：当前的数据结构真的需要它，还是换个标准写法就能写得更稳？答案往往早就藏在控制台的警告日志里。&lt;/p&gt;</description><pubDate>Fri, 22 May 2026 06:00:32 +0800</pubDate></item><item><title>js charAt获取字符</title><link>https://www.tenca.cn/post/javascript-tutorial/7651.html</link><description>&lt;h1&gt;字符串抠细节，别只盯着方括号：聊聊 js charAt 的真实用法&lt;/h1&gt;
&lt;p&gt;写代码久了，总会碰到需要“拆字”的场合。比如判断用户提交的工单编号是否以特定字母开头，或者从一串固定长度的流水号里精准捞出一段业务标识。很多人习惯性掏出方括号 &lt;code&gt;str[0]&lt;/code&gt;，但翻到严格校验逻辑或遗留系统时，还是会频繁撞见 &lt;code&gt;charAt()&lt;/code&gt;。这个方法看着朴素，实际落地时有不少细节值得提前摸清。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;charAt(index)&lt;/code&gt; 的核心逻辑很直白：顺着传入的索引位置，返回对应的单个字符。JavaScript 字符串索引从零起步，&lt;strong&gt;取首个字符必须传 &lt;code&gt;0&lt;/code&gt;&lt;/strong&gt;。它最实用的设计在于静默容错——就算写的索引越界了，它也不会直接抛出异常阻断运行，而是干净地返回空字符串 &lt;code&gt;&quot;&quot;&lt;/code&gt;。在批量清洗脏数据或对接不稳定上游接口时，这省去了大量多余的边界判断语句。&lt;/p&gt;
&lt;p&gt;假设你要拦截格式错误的日期输入，规则要求前两位代表世纪。用 &lt;code&gt;charAt()&lt;/code&gt; 逐位提取的思路会非常清晰：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;let raw = &quot;23-12-31&quot;;
let century = raw.charAt(0) + raw.charAt(1);
// 直接转 Number 做范围约束，无需创建新子串对象&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把零散字符拼接后丢进数字转换，内存分配极少。对于后端交付的长度固定字段，这种按位抽取的方式比整体切片更可控，后续排查问题也更容易定位到具体坐标。&lt;/p&gt;
&lt;p&gt;有人好奇，既然方括号语法更短，为何语言设计还留着 &lt;code&gt;charAt&lt;/code&gt;？两者的执行路径在现代引擎中虽已趋同，但语义指向完全不同。&lt;strong&gt;方括号偏向“数组式访问”，适用于连续遍历或动态索引；&lt;code&gt;charAt&lt;/code&gt; 则明确宣告“我只取一个字符”&lt;/strong&gt;。在多人协作的代码库中，后者能让接手的人瞬间理解你的业务意图，减少误读概率。同时，在部分严格模式或老旧移动端 WebView 里，方括号偶尔会触发隐式的 String 包装对象，&lt;code&gt;charAt&lt;/code&gt; 始终走原生快速路径，稳定性更有保障。&lt;/p&gt;
&lt;p&gt;当然，它也有明确的短板。遇到 Emoji 或生僻字时，&lt;code&gt;charAt&lt;/code&gt; 是按 UTF-16 码元硬切分的，直接取值大概率得到半个乱码。此时需要借助扩展运算符 &lt;code&gt;[...str][i]&lt;/code&gt; 或 &lt;code&gt;Array.from()&lt;/code&gt; 拆解完整字符单元，避免渲染错位。&lt;/p&gt;
&lt;p&gt;实际处理敏感数据脱敏时，组合索引能发挥最大效能。比如手机号中间四位隐藏：&lt;strong&gt;确定保留的首尾坐标 → 依次调用 charAt 拼出明文段 → 插入掩码符号 → 重组返回&lt;/strong&gt;。这种写法跳过了 &lt;code&gt;substring&lt;/code&gt; 产生的临时字符串拷贝，在高频循环或长列表渲染中，能有效压低 GC 压力，保证主线程流畅度。&lt;/p&gt;
&lt;p&gt;工具没有绝对的高低，只有场景契合度。&lt;code&gt;charAt&lt;/code&gt; 并非被时代淘汰的老物件，它在精确定位、静默越界处理和明确语义表达上，依然保持着不可替代的价值。下次再遇到“只需要字符串里某一位”的需求，不妨放下惯性思维，按需选用。写代码就像理线头，捏准那个关键节点，整条链路自然就通了。&lt;/p&gt;</description><pubDate>Fri, 22 May 2026 00:00:29 +0800</pubDate></item><item><title>js replace字符串替换</title><link>https://www.tenca.cn/post/javascript-tutorial/7650.html</link><description>&lt;h1&gt;JS replace 不止于“换词”，掌握这三招搞定高频字符串难题&lt;/h1&gt;
&lt;p&gt;写前端代码时，几乎每个开发者都跟过这么一段需求：“把用户输入里的敏感词过滤掉”或者“把日期格式从 YYYY/MM/DD 转成 YYYY-MM-DD”。很多人第一反应是直接上 &lt;code&gt;replace&lt;/code&gt;，敲着敲着却发现，有时候只换了一个，有时候全乱了。字符串替换看着基础，真要把它用到顺手，得跨过几个经常被忽略的细节。&lt;/p&gt;
&lt;p&gt;默认情况下，&lt;strong&gt;原生 &lt;code&gt;replace&lt;/code&gt; 只认第一个匹配项&lt;/strong&gt;。比如想把手机号中间四位隐藏起来，写成 &lt;code&gt;&quot;13812345678&quot;.replace(&quot;1&quot;, &quot;***&quot;)&lt;/code&gt;，结果只会变成 &lt;code&gt;***3812345678&lt;/code&gt;，剩下的数字纹丝不动。这时候需要给正则表达式加上全局标志 &lt;code&gt;/g&lt;/code&gt;：&lt;strong&gt;用正则配合全局标记才能彻底清扫所有目标&lt;/strong&gt;。正确的做法是把待替换的字符封装成正则字面量，直接写上 &lt;code&gt;/目标字符/g&lt;/code&gt; 再执行替换。记住这个惯性思维盲区，能省下一大段调试时间。&lt;/p&gt;
&lt;p&gt;如果需求稍微复杂点，比如要把商品编号里的字母统一转大写，或者根据匹配到的数字动态计算折扣价，静态字符串就搞不定了。&lt;code&gt;replace&lt;/code&gt; 真正的威力在于&lt;strong&gt;第二个参数可以直接接收一个回调函数&lt;/strong&gt;。当正则命中某段文本时，引擎会把捕获的内容自动传入函数，函数的返回值直接替回原位置。把复杂的条件判断塞进回调体里，替换就不再是机械搬运，而是带着业务规则的精准加工。表单清洗、字段脱敏这类脏活累活，靠这套组合拳就能一次跑通。&lt;/p&gt;
&lt;p&gt;玩透函数回调的同时，经常会碰到分组引用带来的排版混乱。正则里的圆括号会默默记录顺序，&lt;strong&gt;$&amp;amp; 代表整句匹配内容，$1 对应第一组，$' 和 $` 分别表示匹配后的后缀与前缀&lt;/strong&gt;。把这些内置占位符和模板字符串搭配使用，能大幅削减手动拆解拼接的代码量。顺便提一句，现代运行环境已经全面支持 &lt;code&gt;replaceAll()&lt;/code&gt; 方法，语法更直白，不再强依赖正则的全局标志。但在需要动态计算返回值的老项目或复杂校验流里，&lt;code&gt;replace&lt;/code&gt; 配合 &lt;code&gt;/g&lt;/code&gt; 依然是不可替代的主力。新旧工具交替，选对切入点比死记 API 列表更管用。&lt;/p&gt;
&lt;p&gt;字符串替换从来不是孤立的语法片段，而是数据预处理流水线上的第一道闸门。平时多留意那些需要按规则变形的文本场景，把静态替换升级成动态处理，代码结构会清爽不少。下次再遇到“批量改字”的需求，不妨先想想该让正则上场兜底，还是直接把核心逻辑喂给回调函数。练熟这几套实操路径，日常开发里的文字变形工作，基本都能从容应对。&lt;/p&gt;</description><pubDate>Thu, 21 May 2026 18:00:34 +0800</pubDate></item></channel></rss>