首页 文章 精选 留言 我的

精选列表

搜索[JavaScript库],共10000篇文章
优秀的个人博客,低调大师

JavaScript 正则匹配中的 lastIndex 陷阱:为什么 test 和 exec 会交替返回 true 和 false

用同一个正则对象反复测试不同字符串,明明字符串都匹配,却出现一次 true 一次 false 的诡异现象,这是不少人在表单校验、循环过滤或批量替换时踩过的坑。问题大多不在正则模式本身,而是带了 g 或 y 标志的正则对象会维护 lastIndex 状态。调用 test 和 exec 时,匹配成功会更新这个值,下一次从新位置继续找,匹配失败才把它重置为 0。于是跨字符串复用同一实例时就容易出现交替结果。这篇文章从 JS 引擎的执行规则出发,解释 lastIndex 的行为边界,给出几种可靠的修正写法,并说明为什么在 React 组件、工具函数和模块级常量的场景里尤其容易撞上这个坑,以及如何通过代码审查和测试提前发现。 ## 先看一段会翻车的代码 ```js const pattern = /\d+/g; console.log(pattern.test('abc123')); // true console.log(pattern.test('def456')); // false console.log(pattern.test('ghi789')); // true ``` 三个字符串都包含数字,按理都应该返回 true。结果却是 true、false、true 交替。 换个写法,用 exec: ```js const re = /foo/g; re.exec('foo bar'); // ['foo'],lastIndex 变成 3 re.exec('foo bar'); // 从索引 3 开始找不到,返回 null,并把 lastIndex 重置为 0 re.exec('foo bar'); // ['foo'] ``` 现象一致。根因就是 lastIndex。 ## lastIndex 什么时候生效 lastIndex 是正则实例上的一个可读写属性,默认是 0。它只对带 g(global)或 y(sticky)标志的正则起作用。 - 带 g 或 y 时:exec 和 test 从 lastIndex 指定的位置开始搜索。如果找到匹配,lastIndex 被更新为匹配结束的下一个字符索引;如果没找到,lastIndex 重置为 0。 - 不带 g 或 y 时:exec 和 test 始终从字符串开头搜索,并且不会读写 lastIndex。 验证一下: ```js const reG = /a/g; console.log(reG.lastIndex); // 0 reG.test('aa'); console.log(reG.lastIndex); // 1 const reNoFlag = /a/; reNoFlag.test('aa'); console.log(reNoFlag.lastIndex); // 0 ``` 顺带说一个容易忽略的点:`String.prototype.matchAll` 要求正则必须带 g 标志,否则会抛 TypeError。而 `String.prototype.match` 在高版本规范里如果带 g,会一次性返回所有匹配并把 lastIndex 重置为 0,行为跟 exec 不同。 ## 为什么模块级正则最容易出事 把正则提到模块顶层或者组件外部,是常见的优化手段,能避免每次渲染都新建对象。但在带 g 的情况下,这个对象也就变成了全局可变状态。 看一个 React 场景: ```js const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/g; function EmailList({ emails }) { return ( {emails.map((e) => ( {e} ))} ); } ``` emails 里的每一项单独看都是合法邮箱,但渲染出来会有一半标成 bad。因为 map 每次调用 test 都复用了同一个正则实例,lastIndex 在推进。 类似地,在 utill 文件里导出这类正则给多个模块共用,问题会更隐蔽:不同调用方相互干扰,lastIndex 状态谁都说不清。 ## 直接去修正 lastIndex 可行吗 最直接的方式是在每次调用前把它归零: ```js const pattern = /\d+/g; function isNumeric(str) { pattern.lastIndex = 0; return pattern.test(str); } ``` 能用,但有两个代价。一个是每个调用点都得记得写,一旦漏掉就复现原问题;另一个是当代码在多处并发或异步执行时,归零和 test 之间并非原子操作,虽然 JS 单线程里同步代码没事,但混入异步逻辑之后仍然容易读错状态。 只对带 g 的正则这么做。不带 g 时手动改 lastIndex 不起作用,因为引擎不读它。 ## 更稳的做法:不带 g,或者每次新建 如果目的只是“判断某个字符串是否匹配”,绝大多数场景根本不需要 g 标志。 ```js const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; emailRe.test('a@b.co'); // true emailRe.test('c@d.co'); // true ``` 没有 g,test 永远从头匹配,也不会修改 lastIndex,跨调用完全安全。y 标志在单次校验里更是没必要。 需要循环找所有匹配时,用 `matchAll` 或者 `replace` 这类一次性消费的 API,比持有同一个 exec 反复调用更清楚: ```js const text = 'a1 b2 c3'; const re = /([a-z])(\d)/g; for (const m of text.matchAll(re)) { console.log(m[1], m[2]); } ``` matchAll 内部不会污染你传给它的正则的 lastIndex。注意它要求正则带 g,否则抛错。 如果确实需要在循环里用 exec,就把正则放在循环内部新建: ```js function extractNumbers(text) { const re = /\d+/g; const result = []; let m; while ((m = re.exec(text)) !== null) { result.push(m[0]); } return result; } ``` 每次调用新建实例,lastIndex 从 0 开始,自然不会有残留状态。正则字面量在某些引擎里可以复用已编译的模式,实际开销不算大,先保证正确性。 ## 循环 exec 时也别忘了一个细节 即使正则只在函数内新建,while 循环里的 exec 也有个坑:如果匹配到的是空字符串(比如用 `/a*/g` 这类模式),lastIndex 不会前进,循环会变成死循环。 ```js const re = /a*/g; let m; while ((m = re.exec('bbb')) !== null) { // 匹配到空串,lastIndex 一直是 0,永不结束 } ``` 写法要改成匹配空串时手动推进 lastIndex,或者直接换用不带这种模式的方案: ```js const re = /a*/g; let m; while ((m = re.exec('bbb')) !== null) { if (m[0] === '') { re.lastIndex++; } } ``` 提取内容时基本用不上 `/a*/g` 这种能匹配空的模式,但逻辑里拼出来的时候要意识到这一点。 ## 单元测试可以主动兜住这个坑 写测试时,如果被测函数的内部实现用了模块级 g 正则,仅仅测一次调用是抓不到问题的。 ```js import { isNumeric } from './validators'; test('isNumeric 连续调用保持稳定', () => { expect(isNumeric('abc123')).toBe(true); expect(isNumeric('def456')).toBe(true); expect(isNumeric('ghi789')).toBe(true); }); ``` 只要连续断言同一个函数,lastIndex 残留就会被暴露出来。对于共用正则实例的工具模块,建议这类“重复调用”断言作为固定用例。 ## 检索代码时的一个实用技巧 要排查项目里潜在的问题,可以搜索带 g 或 y 标志且用 test 或 exec 的正则字面量。常见特征: ``` /.../g.test( /.../g.exec( new RegExp(...).test( ``` 重点看这些正则定义的位置。如果是在模块顶层或者组件外部,然后被多个字符串反复 test,基本可以判定为隐患。替换方式通常是去掉 g,或者改用在函数内部新建的写法。 这类 bug 不会报错,运行时也不抛异常,只是结果悄悄不对,线上排查成本很高。理解 lastIndex 是正则实例属性而不是字符串相关状态,基本上就绕开了大半。 推荐查看天气:http://www.362tq.com/changning/40tian.html 推荐查看天气:http://www.362tq.com/zhaotong/ 推荐查看天气:http://www.362tq.com/zhaotong/zuotian.html 推荐查看天气:http://www.362tq.com/zhaotong/7tian.html 推荐查看天气:http://www.362tq.com/zhaotong/10tian.html 推荐查看天气:http://www.362tq.com/zhaotong/15tian.html 推荐查看天气:http://www.362tq.com/zhaotong/40tian.html 推荐查看天气:http://www.362tq.com/zhaoyang/ 推荐查看天气:http://www.362tq.com/zhaoyang/zuotian.html 推荐查看天气:http://www.362tq.com/zhaoyang/7tian.html 推荐查看天气:http://www.362tq.com/zhaoyang/10tian.html 推荐查看天气:http://www.362tq.com/zhaoyang/15tian.html 推荐查看天气:http://www.362tq.com/zhaoyang/40tian.html 推荐查看天气:http://www.362tq.com/ludian/ 推荐查看天气:http://www.362tq.com/ludian/zuotian.html 推荐查看天气:http://www.362tq.com/ludian/7tian.html 推荐查看天气:http://www.362tq.com/ludian/10tian.html 推荐查看天气:http://www.362tq.com/ludian/15tian.html 推荐查看天气:http://www.362tq.com/ludian/40tian.html 推荐查看天气:http://www.362tq.com/qiaojia/ 推荐查看天气:http://www.362tq.com/qiaojia/zuotian.html 推荐查看天气:http://www.362tq.com/qiaojia/7tian.html 推荐查看天气:http://www.362tq.com/qiaojia/10tian.html 推荐查看天气:http://www.362tq.com/qiaojia/15tian.html 推荐查看天气:http://www.362tq.com/qiaojia/40tian.html 推荐查看天气:http://www.362tq.com/yanjin/ 推荐查看天气:http://www.362tq.com/yanjin/zuotian.html 推荐查看天气:http://www.362tq.com/yanjin/7tian.html 推荐查看天气:http://www.362tq.com/yanjin/10tian.html 推荐查看天气:http://www.362tq.com/yanjin/15tian.html 推荐查看天气:http://www.362tq.com/yanjin/40tian.html 推荐查看天气:http://www.362tq.com/daguan/ 推荐查看天气:http://www.362tq.com/daguan/zuotian.html 推荐查看天气:http://www.362tq.com/daguan/7tian.html 推荐查看天气:http://www.362tq.com/daguan/10tian.html 推荐查看天气:http://www.362tq.com/daguan/15tian.html 推荐查看天气:http://www.362tq.com/daguan/40tian.html 推荐查看天气:http://www.362tq.com/yongshan/ 推荐查看天气:http://www.362tq.com/yongshan/zuotian.html 推荐查看天气:http://www.362tq.com/yongshan/7tian.html 推荐查看天气:http://www.362tq.com/yongshan/10tian.html 推荐查看天气:http://www.362tq.com/yongshan/15tian.html 推荐查看天气:http://www.362tq.com/yongshan/40tian.html 推荐查看天气:http://www.362tq.com/suijiang/ 推荐查看天气:http://www.362tq.com/suijiang/zuotian.html 推荐查看天气:http://www.362tq.com/suijiang/7tian.html 推荐查看天气:http://www.362tq.com/suijiang/10tian.html 推荐查看天气:http://www.362tq.com/suijiang/15tian.html 推荐查看天气:http://www.362tq.com/suijiang/40tian.html 推荐查看天气:http://www.362tq.com/zhenxiong/ 推荐查看天气:http://www.362tq.com/zhenxiong/zuotian.html 推荐查看天气:http://www.362tq.com/zhenxiong/7tian.html 推荐查看天气:http://www.362tq.com/zhenxiong/10tian.html 推荐查看天气:http://www.362tq.com/zhenxiong/15tian.html 推荐查看天气:http://www.362tq.com/zhenxiong/40tian.html 推荐查看天气:http://www.362tq.com/yiliang/ 推荐查看天气:http://www.362tq.com/yiliang/zuotian.html 推荐查看天气:http://www.362tq.com/yiliang/7tian.html 推荐查看天气:http://www.362tq.com/yiliang/10tian.html 推荐查看天气:http://www.362tq.com/yiliang/15tian.html 推荐查看天气:http://www.362tq.com/yiliang/40tian.html 推荐查看天气:http://www.362tq.com/weixin/ 推荐查看天气:http://www.362tq.com/weixin/zuotian.html 推荐查看天气:http://www.362tq.com/weixin/7tian.html 推荐查看天气:http://www.362tq.com/weixin/10tian.html 推荐查看天气:http://www.362tq.com/weixin/15tian.html 推荐查看天气:http://www.362tq.com/weixin/40tian.html 推荐查看天气:http://www.362tq.com/shuifu/ 推荐查看天气:http://www.362tq.com/shuifu/zuotian.html 推荐查看天气:http://www.362tq.com/shuifu/7tian.html 推荐查看天气:http://www.362tq.com/shuifu/10tian.html 推荐查看天气:http://www.362tq.com/shuifu/15tian.html 推荐查看天气:http://www.362tq.com/shuifu/40tian.html 推荐查看天气:http://www.362tq.com/lijiang/ 推荐查看天气:http://www.362tq.com/lijiang/zuotian.html 推荐查看天气:http://www.362tq.com/lijiang/7tian.html 推荐查看天气:http://www.362tq.com/lijiang/10tian.html 推荐查看天气:http://www.362tq.com/lijiang/15tian.html 推荐查看天气:http://www.362tq.com/lijiang/40tian.html 推荐查看天气:http://www.362tq.com/gucheng/ 推荐查看天气:http://www.362tq.com/gucheng/zuotian.html 推荐查看天气:http://www.362tq.com/gucheng/7tian.html 推荐查看天气:http://www.362tq.com/gucheng/10tian.html 推荐查看天气:http://www.362tq.com/gucheng/15tian.html 推荐查看天气:http://www.362tq.com/gucheng/40tian.html 推荐查看天气:http://www.362tq.com/yulong/ 推荐查看天气:http://www.362tq.com/yulong/zuotian.html 推荐查看天气:http://www.362tq.com/yulong/7tian.html 推荐查看天气:http://www.362tq.com/yulong/10tian.html 推荐查看天气:http://www.362tq.com/yulong/15tian.html 推荐查看天气:http://www.362tq.com/yulong/40tian.html 推荐查看天气:http://www.362tq.com/yongsheng/ 推荐查看天气:http://www.362tq.com/yongsheng/zuotian.html 推荐查看天气:http://www.362tq.com/yongsheng/7tian.html 推荐查看天气:http://www.362tq.com/yongsheng/10tian.html 推荐查看天气:http://www.362tq.com/yongsheng/15tian.html 推荐查看天气:http://www.362tq.com/yongsheng/40tian.html 推荐查看天气:http://www.362tq.com/huaping/ 推荐查看天气:http://www.362tq.com/huaping/zuotian.html 推荐查看天气:http://www.362tq.com/huaping/7tian.html 推荐查看天气:http://www.362tq.com/huaping/10tian.html 推荐查看天气:http://www.362tq.com/huaping/15tian.html 推荐查看天气:http://www.362tq.com/huaping/40tian.html 推荐查看天气:http://www.362tq.com/ninglang/ 推荐查看天气:http://www.362tq.com/ninglang/zuotian.html 推荐查看天气:http://www.362tq.com/ninglang/7tian.html 推荐查看天气:http://www.362tq.com/ninglang/10tian.html 推荐查看天气:http://www.362tq.com/ninglang/15tian.html 推荐查看天气:http://www.362tq.com/ninglang/40tian.html 推荐查看天气:http://www.362tq.com/puer/ 推荐查看天气:http://www.362tq.com/puer/zuotian.html 推荐查看天气:http://www.362tq.com/puer/7tian.html 推荐查看天气:http://www.362tq.com/puer/10tian.html 推荐查看天气:http://www.362tq.com/puer/15tian.html 推荐查看天气:http://www.362tq.com/puer/40tian.html 推荐查看天气:http://www.362tq.com/simao/ 推荐查看天气:http://www.362tq.com/simao/zuotian.html 推荐查看天气:http://www.362tq.com/simao/7tian.html 推荐查看天气:http://www.362tq.com/simao/10tian.html 推荐查看天气:http://www.362tq.com/simao/15tian.html 推荐查看天气:http://www.362tq.com/simao/40tian.html 推荐查看天气:http://www.362tq.com/ninger/ 推荐查看天气:http://www.362tq.com/ninger/zuotian.html 推荐查看天气:http://www.362tq.com/ninger/7tian.html 推荐查看天气:http://www.362tq.com/ninger/10tian.html 推荐查看天气:http://www.362tq.com/ninger/15tian.html 推荐查看天气:http://www.362tq.com/ninger/40tian.html 推荐查看天气:http://www.362tq.com/mojiang/ 推荐查看天气:http://www.362tq.com/mojiang/zuotian.html 推荐查看天气:http://www.362tq.com/mojiang/7tian.html 推荐查看天气:http://www.362tq.com/mojiang/10tian.html 推荐查看天气:http://www.362tq.com/mojiang/15tian.html 推荐查看天气:http://www.362tq.com/mojiang/40tian.html 推荐查看天气:http://www.362tq.com/jingdong/ 推荐查看天气:http://www.362tq.com/jingdong/zuotian.html 推荐查看天气:http://www.362tq.com/jingdong/7tian.html 推荐查看天气:http://www.362tq.com/jingdong/10tian.html 推荐查看天气:http://www.362tq.com/jingdong/15tian.html 推荐查看天气:http://www.362tq.com/jingdong/40tian.html 推荐查看天气:http://www.362tq.com/jinggu/ 推荐查看天气:http://www.362tq.com/jinggu/zuotian.html 推荐查看天气:http://www.362tq.com/jinggu/7tian.html 推荐查看天气:http://www.362tq.com/jinggu/10tian.html 推荐查看天气:http://www.362tq.com/jinggu/15tian.html 推荐查看天气:http://www.362tq.com/jinggu/40tian.html 推荐查看天气:http://www.362tq.com/simaozhenyuan/ 推荐查看天气:http://www.362tq.com/simaozhenyuan/zuotian.html 推荐查看天气:http://www.362tq.com/simaozhenyuan/7tian.html 推荐查看天气:http://www.362tq.com/simaozhenyuan/10tian.html 推荐查看天气:http://www.362tq.com/simaozhenyuan/15tian.html 推荐查看天气:http://www.362tq.com/simaozhenyuan/40tian.html 推荐查看天气:http://www.362tq.com/jiangcheng/ 推荐查看天气:http://www.362tq.com/jiangcheng/zuotian.html 推荐查看天气:http://www.362tq.com/jiangcheng/7tian.html 推荐查看天气:http://www.362tq.com/jiangcheng/10tian.html 推荐查看天气:http://www.362tq.com/jiangcheng/15tian.html 推荐查看天气:http://www.362tq.com/jiangcheng/40tian.html 推荐查看天气:http://www.362tq.com/menglian/ 推荐查看天气:http://www.362tq.com/menglian/zuotian.html 推荐查看天气:http://www.362tq.com/menglian/7tian.html 推荐查看天气:http://www.362tq.com/menglian/10tian.html 推荐查看天气:http://www.362tq.com/menglian/15tian.html 推荐查看天气:http://www.362tq.com/menglian/40tian.html 推荐查看天气:http://www.362tq.com/lancang/ 推荐查看天气:http://www.362tq.com/lancang/zuotian.html 推荐查看天气:http://www.362tq.com/lancang/7tian.html 推荐查看天气:http://www.362tq.com/lancang/10tian.html 推荐查看天气:http://www.362tq.com/lancang/15tian.html 推荐查看天气:http://www.362tq.com/lancang/40tian.html 推荐查看天气:http://www.362tq.com/ximeng/ 推荐查看天气:http://www.362tq.com/ximeng/zuotian.html 推荐查看天气:http://www.362tq.com/ximeng/7tian.html 推荐查看天气:http://www.362tq.com/ximeng/10tian.html 推荐查看天气:http://www.362tq.com/ximeng/15tian.html 推荐查看天气:http://www.362tq.com/ximeng/40tian.html 推荐查看天气:http://www.362tq.com/lincang/ 推荐查看天气:http://www.362tq.com/lincang/zuotian.html 推荐查看天气:http://www.362tq.com/lincang/7tian.html 推荐查看天气:http://www.362tq.com/lincang/10tian.html 推荐查看天气:http://www.362tq.com/lincang/15tian.html 推荐查看天气:http://www.362tq.com/lincang/40tian.html 推荐查看天气:http://www.362tq.com/linxiang/ 推荐查看天气:http://www.362tq.com/linxiang/zuotian.html 推荐查看天气:http://www.362tq.com/linxiang/7tian.html 推荐查看天气:http://www.362tq.com/linxiang/10tian.html 推荐查看天气:http://www.362tq.com/linxiang/15tian.html 推荐查看天气:http://www.362tq.com/linxiang/40tian.html 推荐查看天气:http://www.362tq.com/fengqing/ 推荐查看天气:http://www.362tq.com/fengqing/zuotian.html 推荐查看天气:http://www.362tq.com/fengqing/7tian.html 推荐查看天气:http://www.362tq.com/fengqing/10tian.html 推荐查看天气:http://www.362tq.com/fengqing/15tian.html 推荐查看天气:http://www.362tq.com/fengqing/40tian.html 推荐查看天气:http://www.362tq.com/yunxian/ 推荐查看天气:http://www.362tq.com/yunxian/zuotian.html 推荐查看天气:http://www.362tq.com/yunxian/7tian.html 推荐查看天气:http://www.362tq.com/yunxian/10tian.html 推荐查看天气:http://www.362tq.com/yunxian/15tian.html 推荐查看天气:http://www.362tq.com/yunxian/40tian.html 推荐查看天气:http://www.362tq.com/yongde/

优秀的个人博客,低调大师

JavaScript 中 structuredClone 的实战坑:为什么你的深拷贝在 class 实例上翻车了?

在维护一个后台管理系统时,我需要把一份包含日期、Map 和自定义 class 实例的配置对象深拷贝到草稿状态里。以前一直用 JSON.parse(JSON.stringify(obj)),但它会把 Date 变成字符串、丢掉 undefined,更麻烦的是遇到 class 实例直接变成普通对象,方法全没了。后来我换成了浏览器原生的 structuredClone,大部分场景确实好使,但有一次拷贝一个带 getter 的 class 实例时,拷贝出来的对象属性直接变成 undefined,排查了半天才发现 structuredClone 虽然能处理循环引用和内置类型,但它不会保留原型链。这篇文章从实际代码出发,讲清楚 structuredClone 和 JSON 深拷贝的边界在哪、哪些数据结构它会静默丢信息、以及怎么用 MessageChannel 或手写递归补上原型和 getter 的坑,顺便对比 lodash.cloneDeep 在 class 场景下的表现。 ```js class User { constructor(name) { this.name = name; this.createdAt = new Date(); } get displayName() { return `用户:${this.name}`; } greet() { return `hello ${this.name}`; } } const u = new User('alice'); const cloned = structuredClone(u); console.log(cloned instanceof User); // false console.log(cloned.displayName); // undefined console.log(typeof cloned.greet); // 'undefined' console.log(cloned.name); // 'alice' ``` 上面这段代码说明问题:structuredClone 会老老实实复制自有属性,但原型上的 getter 和方法完全不会被带过去。它内部用的是结构化克隆算法,只搬运“数据”,不搬运行为。如果你把 class 实例当作纯数据容器,那没问题;一旦代码依赖原型方法,拷贝结果就是残缺的。 再看它和 JSON 方案的差异。structuredClone 能正确处理 Date、RegExp、Map、Set、ArrayBuffer、循环引用,这些是 JSON 方案做不到的。 ```js const source = { date: new Date(), map: new Map([['a', 1]]), set: new Set([1, 2]), regex: /abc/g, buffer: new ArrayBuffer(8), }; source.self = source; // 循环引用 const cloned = structuredClone(source); console.log(cloned.date instanceof Date); // true console.log(cloned.map.get('a')); // 1 console.log(cloned.self === cloned); // true ``` JSON 方案遇到循环引用直接抛 TypeError,遇到 Map 会变成空对象 `{}`,遇到 Date 变成 ISO 字符串。所以只要数据结构里出现这些类型,structuredClone 是首选。但它也有明确不支持的:函数、DOM 节点、Error 的某些子类、以及带原型的对象。函数会直接抛 DataCloneError。 ```js structuredClone({ fn: () => {} }); // Uncaught DOMException: Failed to execute 'structuredClone' on 'Window': () => {} could not be cloned. ``` 实际项目里更隐蔽的坑是 getter。structuredClone 在复制时会对源对象做一次“读属性”操作,如果 getter 内部有副作用或者依赖 this 上的其他状态,读出来的值可能和你预期不同。而且拷贝结果里 getter 会退化成普通数据属性,后续不再动态计算。 ```js class Config { constructor() { this._base = 10; } get total() { return this._base * 2; } } const c = new Config(); const copy = structuredClone(c); console.log(copy.total); // 20,但已经是普通属性 copy._base = 100; console.log(copy.total); // 仍然是 20,不再联动 ``` 如果业务确实需要保留原型和 getter,可以借助 MessageChannel 做一次结构化克隆,再手动把原型接回去。但更直接的办法是给 class 写一个 `clone()` 方法,显式重建实例。 ```js class User { constructor(name, createdAt = new Date()) { this.name = name; this.createdAt = createdAt; } clone() { return new User(this.name, new Date(this.createdAt)); } } const u = new User('bob'); const copy = u.clone(); console.log(copy instanceof User); // true console.log(copy.greet()); // hello bob ``` 对于嵌套层级深、class 类型多的配置对象,lodash 的 `cloneDeep` 会保留原型,但它对 Map、Set 的支持要看版本,早期版本直接当普通对象处理。在 lodash 4.17 之后 `cloneDeep` 能正确复制 Map 和 Set,但循环引用和 Symbol 键仍然有边界情况。所以选型时先看清数据结构:纯 JSON 数据用 JSON 方案最快;含 Date/Map/Set/循环引用用 structuredClone;需要保留原型和方法就手写 clone 或组合使用。 还有一个容易忽略的点:structuredClone 是同步的,如果拷贝的对象特别大,比如几十兆的 ArrayBuffer,主线程会卡住。这种场景可以放到 Web Worker 里做,或者用 `postMessage` 天然走结构化克隆,顺便把计算移出主线程。 ```js // worker.js self.onmessage = (e) => { const cloned = structuredClone(e.data); self.postMessage(cloned); }; ``` 浏览器兼容性方面,Chrome 98+、Firefox 94+、Safari 15.4+ 都支持 structuredClone。Node.js 从 17 开始提供全局 structuredClone,之前版本需要 `require('node:v8').serialize` 做替代,但 v8 序列化不支持循环引用以外的某些类型。如果项目还要兼容旧环境,建议封装一层降级逻辑。 ```js function deepClone(value) { if (typeof structuredClone === 'function') { return structuredClone(value); } return JSON.parse(JSON.stringify(value)); } ``` 这个降级只适合纯数据场景,别指望它处理 Date 和 Map。真正稳妥的做法还是根据数据类型走不同分支,而不是无脑深拷贝。很多情况下,避免深拷贝、改用不可变更新反而更省心。 推荐查看天气:https://www.40tianqi.com/xichuan/ 推荐查看天气:https://www.40tianqi.com/xichuan/zuotian.html 推荐查看天气:https://www.40tianqi.com/xichuan/7tian.html 推荐查看天气:https://www.40tianqi.com/xichuan/10tian.html 推荐查看天气:https://www.40tianqi.com/xichuan/15tian.html 推荐查看天气:https://www.40tianqi.com/xichuan/40tian.html 推荐查看天气:https://www.40tianqi.com/sheqi/ 推荐查看天气:https://www.40tianqi.com/sheqi/zuotian.html 推荐查看天气:https://www.40tianqi.com/sheqi/7tian.html 推荐查看天气:https://www.40tianqi.com/sheqi/10tian.html 推荐查看天气:https://www.40tianqi.com/sheqi/15tian.html 推荐查看天气:https://www.40tianqi.com/sheqi/40tian.html 推荐查看天气:https://www.40tianqi.com/tanghe/ 推荐查看天气:https://www.40tianqi.com/tanghe/zuotian.html 推荐查看天气:https://www.40tianqi.com/tanghe/7tian.html 推荐查看天气:https://www.40tianqi.com/tanghe/10tian.html 推荐查看天气:https://www.40tianqi.com/tanghe/15tian.html 推荐查看天气:https://www.40tianqi.com/tanghe/40tian.html 推荐查看天气:https://www.40tianqi.com/xinye/ 推荐查看天气:https://www.40tianqi.com/xinye/zuotian.html 推荐查看天气:https://www.40tianqi.com/xinye/7tian.html 推荐查看天气:https://www.40tianqi.com/xinye/10tian.html 推荐查看天气:https://www.40tianqi.com/xinye/15tian.html 推荐查看天气:https://www.40tianqi.com/xinye/40tian.html 推荐查看天气:https://www.40tianqi.com/tongbai/ 推荐查看天气:https://www.40tianqi.com/tongbai/zuotian.html 推荐查看天气:https://www.40tianqi.com/tongbai/7tian.html 推荐查看天气:https://www.40tianqi.com/tongbai/10tian.html 推荐查看天气:https://www.40tianqi.com/tongbai/15tian.html 推荐查看天气:https://www.40tianqi.com/tongbai/40tian.html 推荐查看天气:https://www.40tianqi.com/dengzhou/ 推荐查看天气:https://www.40tianqi.com/dengzhou/zuotian.html 推荐查看天气:https://www.40tianqi.com/dengzhou/7tian.html 推荐查看天气:https://www.40tianqi.com/dengzhou/10tian.html 推荐查看天气:https://www.40tianqi.com/dengzhou/15tian.html 推荐查看天气:https://www.40tianqi.com/dengzhou/40tian.html 推荐查看天气:https://www.40tianqi.com/shangqiu/ 推荐查看天气:https://www.40tianqi.com/shangqiu/zuotian.html 推荐查看天气:https://www.40tianqi.com/shangqiu/7tian.html 推荐查看天气:https://www.40tianqi.com/shangqiu/10tian.html 推荐查看天气:https://www.40tianqi.com/shangqiu/15tian.html 推荐查看天气:https://www.40tianqi.com/shangqiu/40tian.html 推荐查看天气:https://www.40tianqi.com/liangyuan/ 推荐查看天气:https://www.40tianqi.com/liangyuan/zuotian.html 推荐查看天气:https://www.40tianqi.com/liangyuan/7tian.html 推荐查看天气:https://www.40tianqi.com/liangyuan/10tian.html 推荐查看天气:https://www.40tianqi.com/liangyuan/15tian.html 推荐查看天气:https://www.40tianqi.com/liangyuan/40tian.html 推荐查看天气:https://www.40tianqi.com/minquan/ 推荐查看天气:https://www.40tianqi.com/minquan/zuotian.html 推荐查看天气:https://www.40tianqi.com/minquan/7tian.html 推荐查看天气:https://www.40tianqi.com/minquan/10tian.html 推荐查看天气:https://www.40tianqi.com/minquan/15tian.html 推荐查看天气:https://www.40tianqi.com/minquan/40tian.html 推荐查看天气:https://www.40tianqi.com/suixian/ 推荐查看天气:https://www.40tianqi.com/suixian/zuotian.html 推荐查看天气:https://www.40tianqi.com/suixian/7tian.html 推荐查看天气:https://www.40tianqi.com/suixian/10tian.html 推荐查看天气:https://www.40tianqi.com/suixian/15tian.html 推荐查看天气:https://www.40tianqi.com/suixian/40tian.html 推荐查看天气:https://www.40tianqi.com/ningling/ 推荐查看天气:https://www.40tianqi.com/ningling/zuotian.html 推荐查看天气:https://www.40tianqi.com/ningling/7tian.html 推荐查看天气:https://www.40tianqi.com/ningling/10tian.html 推荐查看天气:https://www.40tianqi.com/ningling/15tian.html 推荐查看天气:https://www.40tianqi.com/ningling/40tian.html 推荐查看天气:https://www.40tianqi.com/zhecheng/ 推荐查看天气:https://www.40tianqi.com/zhecheng/zuotian.html 推荐查看天气:https://www.40tianqi.com/zhecheng/7tian.html 推荐查看天气:https://www.40tianqi.com/zhecheng/10tian.html 推荐查看天气:https://www.40tianqi.com/zhecheng/15tian.html 推荐查看天气:https://www.40tianqi.com/zhecheng/40tian.html 推荐查看天气:https://www.40tianqi.com/shangqiuyucheng/ 推荐查看天气:https://www.40tianqi.com/shangqiuyucheng/zuotian.html 推荐查看天气:https://www.40tianqi.com/shangqiuyucheng/7tian.html 推荐查看天气:https://www.40tianqi.com/shangqiuyucheng/10tian.html 推荐查看天气:https://www.40tianqi.com/shangqiuyucheng/15tian.html 推荐查看天气:https://www.40tianqi.com/shangqiuyucheng/40tian.html 推荐查看天气:https://www.40tianqi.com/xiayi/ 推荐查看天气:https://www.40tianqi.com/xiayi/zuotian.html 推荐查看天气:https://www.40tianqi.com/xiayi/7tian.html 推荐查看天气:https://www.40tianqi.com/xiayi/10tian.html 推荐查看天气:https://www.40tianqi.com/xiayi/15tian.html 推荐查看天气:https://www.40tianqi.com/xiayi/40tian.html 推荐查看天气:https://www.40tianqi.com/xinyang/ 推荐查看天气:https://www.40tianqi.com/xinyang/zuotian.html 推荐查看天气:https://www.40tianqi.com/xinyang/7tian.html 推荐查看天气:https://www.40tianqi.com/xinyang/10tian.html 推荐查看天气:https://www.40tianqi.com/xinyang/15tian.html 推荐查看天气:https://www.40tianqi.com/xinyang/40tian.html 推荐查看天气:https://www.40tianqi.com/shihe/ 推荐查看天气:https://www.40tianqi.com/shihe/zuotian.html 推荐查看天气:https://www.40tianqi.com/shihe/7tian.html 推荐查看天气:https://www.40tianqi.com/shihe/10tian.html 推荐查看天气:https://www.40tianqi.com/shihe/15tian.html 推荐查看天气:https://www.40tianqi.com/shihe/40tian.html 推荐查看天气:https://www.40tianqi.com/pingqiao/ 推荐查看天气:https://www.40tianqi.com/pingqiao/zuotian.html 推荐查看天气:https://www.40tianqi.com/pingqiao/7tian.html 推荐查看天气:https://www.40tianqi.com/pingqiao/10tian.html 推荐查看天气:https://www.40tianqi.com/pingqiao/15tian.html 推荐查看天气:https://www.40tianqi.com/pingqiao/40tian.html 推荐查看天气:https://www.40tianqi.com/luoshan/ 推荐查看天气:https://www.40tianqi.com/luoshan/zuotian.html 推荐查看天气:https://www.40tianqi.com/luoshan/7tian.html 推荐查看天气:https://www.40tianqi.com/luoshan/10tian.html 推荐查看天气:https://www.40tianqi.com/luoshan/15tian.html 推荐查看天气:https://www.40tianqi.com/luoshan/40tian.html 推荐查看天气:https://www.40tianqi.com/guangshan/ 推荐查看天气:https://www.40tianqi.com/guangshan/zuotian.html 推荐查看天气:https://www.40tianqi.com/guangshan/7tian.html 推荐查看天气:https://www.40tianqi.com/guangshan/10tian.html 推荐查看天气:https://www.40tianqi.com/guangshan/15tian.html 推荐查看天气:https://www.40tianqi.com/guangshan/40tian.html 推荐查看天气:https://www.40tianqi.com/xinyangxinxian/ 推荐查看天气:https://www.40tianqi.com/xinyangxinxian/zuotian.html 推荐查看天气:https://www.40tianqi.com/xinyangxinxian/7tian.html 推荐查看天气:https://www.40tianqi.com/xinyangxinxian/10tian.html 推荐查看天气:https://www.40tianqi.com/xinyangxinxian/15tian.html 推荐查看天气:https://www.40tianqi.com/xinyangxinxian/40tian.html 推荐查看天气:https://www.40tianqi.com/shangcheng/ 推荐查看天气:https://www.40tianqi.com/shangcheng/zuotian.html 推荐查看天气:https://www.40tianqi.com/shangcheng/7tian.html 推荐查看天气:https://www.40tianqi.com/shangcheng/10tian.html 推荐查看天气:https://www.40tianqi.com/shangcheng/15tian.html 推荐查看天气:https://www.40tianqi.com/shangcheng/40tian.html 推荐查看天气:https://www.40tianqi.com/gushi/ 推荐查看天气:https://www.40tianqi.com/gushi/zuotian.html 推荐查看天气:https://www.40tianqi.com/gushi/7tian.html 推荐查看天气:https://www.40tianqi.com/gushi/10tian.html 推荐查看天气:https://www.40tianqi.com/gushi/15tian.html 推荐查看天气:https://www.40tianqi.com/gushi/40tian.html 推荐查看天气:https://www.40tianqi.com/huangchuan/ 推荐查看天气:https://www.40tianqi.com/huangchuan/zuotian.html 推荐查看天气:https://www.40tianqi.com/huangchuan/7tian.html 推荐查看天气:https://www.40tianqi.com/huangchuan/10tian.html 推荐查看天气:https://www.40tianqi.com/huangchuan/15tian.html 推荐查看天气:https://www.40tianqi.com/huangchuan/40tian.html 推荐查看天气:https://www.40tianqi.com/huaibin/ 推荐查看天气:https://www.40tianqi.com/huaibin/zuotian.html 推荐查看天气:https://www.40tianqi.com/huaibin/7tian.html 推荐查看天气:https://www.40tianqi.com/huaibin/10tian.html 推荐查看天气:https://www.40tianqi.com/huaibin/15tian.html 推荐查看天气:https://www.40tianqi.com/huaibin/40tian.html 推荐查看天气:https://www.40tianqi.com/xixian/ 推荐查看天气:https://www.40tianqi.com/xixian/zuotian.html 推荐查看天气:https://www.40tianqi.com/xixian/7tian.html 推荐查看天气:https://www.40tianqi.com/xixian/10tian.html 推荐查看天气:https://www.40tianqi.com/xixian/15tian.html 推荐查看天气:https://www.40tianqi.com/xixian/40tian.html 推荐查看天气:https://www.40tianqi.com/zhoukou/ 推荐查看天气:https://www.40tianqi.com/zhoukou/zuotian.html 推荐查看天气:https://www.40tianqi.com/zhoukou/7tian.html 推荐查看天气:https://www.40tianqi.com/zhoukou/10tian.html 推荐查看天气:https://www.40tianqi.com/zhoukou/15tian.html 推荐查看天气:https://www.40tianqi.com/zhoukou/40tian.html 推荐查看天气:https://www.40tianqi.com/chuanhui/ 推荐查看天气:https://www.40tianqi.com/chuanhui/zuotian.html 推荐查看天气:https://www.40tianqi.com/chuanhui/7tian.html 推荐查看天气:https://www.40tianqi.com/chuanhui/10tian.html 推荐查看天气:https://www.40tianqi.com/chuanhui/15tian.html 推荐查看天气:https://www.40tianqi.com/chuanhui/40tian.html 推荐查看天气:https://www.40tianqi.com/fugou/ 推荐查看天气:https://www.40tianqi.com/fugou/zuotian.html 推荐查看天气:https://www.40tianqi.com/fugou/7tian.html 推荐查看天气:https://www.40tianqi.com/fugou/10tian.html 推荐查看天气:https://www.40tianqi.com/fugou/15tian.html 推荐查看天气:https://www.40tianqi.com/fugou/40tian.html 推荐查看天气:https://www.40tianqi.com/xihua/ 推荐查看天气:https://www.40tianqi.com/xihua/zuotian.html 推荐查看天气:https://www.40tianqi.com/xihua/7tian.html 推荐查看天气:https://www.40tianqi.com/xihua/10tian.html 推荐查看天气:https://www.40tianqi.com/xihua/15tian.html 推荐查看天气:https://www.40tianqi.com/xihua/40tian.html 推荐查看天气:https://www.40tianqi.com/shangshui/ 推荐查看天气:https://www.40tianqi.com/shangshui/zuotian.html 推荐查看天气:https://www.40tianqi.com/shangshui/7tian.html 推荐查看天气:https://www.40tianqi.com/shangshui/10tian.html 推荐查看天气:https://www.40tianqi.com/shangshui/15tian.html 推荐查看天气:https://www.40tianqi.com/shangshui/40tian.html 推荐查看天气:https://www.40tianqi.com/shenqiu/

优秀的个人博客,低调大师

JavaScript 里 Promise 链中 then 回调抛错后,后面的 catch 为什么有时接不住?

写业务代码时经常遇到这种状况:一个接口请求失败后,你在 `.then()` 里做了错误处理,但代码还是走到了全局的 `unhandledrejection` 事件,控制台飘红。或者反过来,明明某个 `.then()` 里 `throw new Error()` 了,后面的 `.catch()` 却像没看见一样,最后错误跑到了另一个分支里。这不是 Promise 实现有 bug,而是很多人对 then 链的返回值传递和错误冒泡机制理解得不够完整。Promise 的错误处理依赖链式调用中每个环节的返回状态,一旦在某个 `then` 里把 rejection 消化掉,或者返回了一个新的 Promise 把错误吞了,后续的 catch 就永远接不到。这篇文章会从几个真实的代码片段出发,梳理 then 链里错误是怎么一级一级往下传的,以及为什么有时 catch 写了却没生效。 ## 一个容易踩的坑:在 then 里 return 了新的 Promise 先看一段常见的登录逻辑: ```js function login(username, password) { return fetch('/api/login', { method: 'POST', body: JSON.stringify({ username, password }) }) .then(res => res.json()) .then(data => { if (data.code !== 0) { // 这里只是返回了一个 rejected 的 Promise return Promise.reject(new Error(data.message)) } return data.token }) .catch(err => { console.error('login failed:', err) throw err }) } login('foo', 'bar') .then(token => { // 期望在这里拿到 token }) .catch(err => { // 期望在这里处理登录错误 }) ``` 这段代码本身没有大问题,`login` 内部的 `.catch` 把错误重新抛出去,所以外层的 `.catch` 能接到。但很多人在写的时候会在 `catch` 里返回 `false` 或者一个默认值,比如: ```js .catch(err => { console.error(err) return null }) ``` 这个时候链式调用的状态从 rejected 变成了 fulfilled,外层的 `.then(token => ...)` 会被执行,token 是 `null`,而外层后面的 `.catch` 根本不会触发。如果你没在 `then` 里检查 token 是否为空,后续逻辑就可能把 null 传下去,引发更难排查的问题。 ## 错误在 then 链里的传递规则 理解错误传递,只要记住两件事: 1. `.then(onFulfilled, onRejected)` 里如果 `onFulfilled` 抛出异常,返回的 Promise 会变成 rejected;如果 `onRejected` 抛出异常,同样变成 rejected;如果 `onRejected` 返回一个普通值,Promise 会变成 fulfilled。 2. `.catch(onRejected)` 本质上是 `.then(undefined, onRejected)`,所以它也只能捕获它前面那个 Promise 的 rejection。 看一个对比: ```js Promise.reject(new Error('a')) .then(() => console.log('then')) .catch(err => { console.log('catch1', err.message) // catch1 a // 没有 throw,返回 undefined }) .catch(err => { console.log('catch2', err.message) // 不会执行 }) ``` 第一个 `catch` 返回了 `undefined`,相当于把错误处理掉了,后面就没有 rejection 再往下传。如果希望后面的 catch 还能拿到,就必须在 catch 里重新 throw: ```js Promise.reject(new Error('a')) .catch(err => { console.log('catch1', err.message) throw err }) .catch(err => { console.log('catch2', err.message) // catch2 a }) ``` 这个规则本身不复杂,但在嵌套 Promise 或者多个 then 串联时容易混乱,因为每个 then 都会产生一个新的 Promise。 ## 嵌套 Promise 里的错误不会自动冒泡到外层链 有时候我们会这样写: ```js fetch('/api/user') .then(res => res.json()) .then(user => { // 又发起一个请求,但没有 return fetch(`/api/orders/${user.id}`) .then(res => res.json()) .then(orders => { renderOrders(orders) }) }) .catch(err => { console.error('something wrong', err) }) ``` 如果 `/api/orders` 那个请求失败了,内层的 Promise 会 reject,但因为没有 `return`,外层链根本不知道这个新 Promise 的存在。内层的 rejection 会变成一个未处理的 Promise rejection,触发 `unhandledrejection`,而外层的 `.catch` 完全接不到。 正确的做法是把这个内层 Promise 返回出去,让链式调用保持扁平: ```js fetch('/api/user') .then(res => res.json()) .then(user => { return fetch(`/api/orders/${user.id}`) .then(res => res.json()) }) .then(orders => { renderOrders(orders) }) .catch(err => { console.error('something wrong', err) }) ``` 这样内层的错误才会沿着 Promise 链传到外层 catch。 ## async/await 里同样的逻辑 用 `async/await` 时,错误传递规则没变,只是写法不同。`await` 一个 rejected Promise 会抛出异常,可以用 `try/catch` 捕获: ```js async function loadUser() { try { const res = await fetch('/api/user') const user = await res.json() return user } catch (err) { console.error('load user failed', err) throw err } } ``` 如果在 `catch` 里没有 `throw err`,那么 `loadUser` 返回的 Promise 会变成 fulfilled,调用方用 `.catch` 是接不到的。这和前面 `.then` 链的行为一致。 还有一个容易忽略的点:在 `async` 函数里,如果你在 `await` 之后同步抛出一个错误,和 `await` 一个 rejected Promise 一样,都会让函数返回 rejected Promise。 ```js async function getData() { const res = await fetch('/api/data') if (!res.ok) { throw new Error('request failed') // 函数返回 rejected Promise } return res.json() } getData() .then(data => console.log(data)) .catch(err => console.error(err)) // 能接住 ``` 但如果调用方没有写 catch,并且也没有用 `try/catch` 包住 await,错误就会变成 `unhandledrejection`。 ## 什么时候该重新 throw,什么时候该消化错误 这其实是一个业务决策,不是语法问题。判断标准很简单:当前这个 catch 能不能把错误处理完整? 比如在某个子流程里,请求配置失败,但可以降级到本地缓存,这种错误就应该被消化掉,不要往外抛: ```js async function getConfig() { try { const res = await fetch('/api/config') return await res.json() } catch (err) { console.warn('remote config failed, fallback to local') return localConfig } } ``` 如果当前层只是记录日志,并不具备恢复能力,就应该把错误继续抛出去,让上层决定怎么处理: ```js async function updateProfile(data) { try { await api.put('/profile', data) } catch (err) { logError(err) throw err // 让调用方知道更新失败 } } ``` 最怕的是在中间层既没有恢复,又默默返回了 `undefined`,导致上层拿到一个“成功但没数据”的结果,后续逻辑全乱。 ## 调试 unhandledrejection 的一个实用手段 Chrome DevTools 的 Console 里可以打开 “Pause on caught exceptions” 和 “Pause on uncaught exceptions”,配合 `window.addEventListener('unhandledrejection', event => { ... })` 来定位是哪个 Promise 没有被处理。 ```js window.addEventListener('unhandledrejection', event => { // event.reason 是 rejection 的原因 console.error('Unhandled rejection:', event.reason) // 可以在这里上报监控 }) ``` 在 Node.js 里类似: ```js process.on('unhandledRejection', (reason, promise) => { console.error('Unhandled rejection at:', promise, 'reason:', reason) }) ``` 但要注意,`unhandledrejection` 只在 rejection 发生后没有任何处理程序时才会触发。如果在一个 Promise 之后很晚才加上 catch,浏览器可能已经触发过这个事件了。所以生产环境里更推荐在 Promise 链的末端统一处理,而不是依赖全局事件兜底。 ## 一个实际场景:多个并发请求里某个失败 用 `Promise.all` 时,只要有一个 Promise reject,整体就会 reject,其他 Promise 的结果会被忽略。如果想让每个请求独立处理错误,可以用 `Promise.allSettled`: ```js const results = await Promise.allSettled([ fetch('/api/a').then(res => res.json()), fetch('/api/b').then(res => res.json()), fetch('/api/c').then(res => res.json()) ]) results.forEach((result, index) => { if (result.status === 'fulfilled') { console.log(`request ${index} success:`, result.value) } else { console.error(`request ${index} failed:`, result.reason) } }) ``` 如果坚持用 `Promise.all`,就需要在单个请求的 catch 里决定是重新抛出让整体失败,还是返回一个占位值让整体继续。两种选择没有对错,取决于业务是否允许部分数据缺失。 Promise 的错误处理看起来只是 `.catch` 的位置问题,实际写起来会因为返回值、嵌套、async 函数的隐式转换产生很多分支。遇到 catch 接不住的情况,先检查前一个环节是不是把错误消化了,再检查有没有漏掉 return。多数时候问题就在这两处。 推荐查看天气:https://www.0dtq.com/dongtou/ 推荐查看天气:https://www.0dtq.com/dongtou/zuotian.html 推荐查看天气:https://www.0dtq.com/dongtou/7tian.html 推荐查看天气:https://www.0dtq.com/dongtou/10tian.html 推荐查看天气:https://www.0dtq.com/dongtou/15tian.html 推荐查看天气:https://www.0dtq.com/dongtou/40tian.html 推荐查看天气:https://www.0dtq.com/yongjia/ 推荐查看天气:https://www.0dtq.com/yongjia/zuotian.html 推荐查看天气:https://www.0dtq.com/yongjia/7tian.html 推荐查看天气:https://www.0dtq.com/yongjia/10tian.html 推荐查看天气:https://www.0dtq.com/yongjia/15tian.html 推荐查看天气:https://www.0dtq.com/yongjia/40tian.html 推荐查看天气:https://www.0dtq.com/pingyang/ 推荐查看天气:https://www.0dtq.com/pingyang/zuotian.html 推荐查看天气:https://www.0dtq.com/pingyang/7tian.html 推荐查看天气:https://www.0dtq.com/pingyang/10tian.html 推荐查看天气:https://www.0dtq.com/pingyang/15tian.html 推荐查看天气:https://www.0dtq.com/pingyang/40tian.html 推荐查看天气:https://www.0dtq.com/cangnan/ 推荐查看天气:https://www.0dtq.com/cangnan/zuotian.html 推荐查看天气:https://www.0dtq.com/cangnan/7tian.html 推荐查看天气:https://www.0dtq.com/cangnan/10tian.html 推荐查看天气:https://www.0dtq.com/cangnan/15tian.html 推荐查看天气:https://www.0dtq.com/cangnan/40tian.html 推荐查看天气:https://www.0dtq.com/wencheng/ 推荐查看天气:https://www.0dtq.com/wencheng/zuotian.html 推荐查看天气:https://www.0dtq.com/wencheng/7tian.html 推荐查看天气:https://www.0dtq.com/wencheng/10tian.html 推荐查看天气:https://www.0dtq.com/wencheng/15tian.html 推荐查看天气:https://www.0dtq.com/wencheng/40tian.html 推荐查看天气:https://www.0dtq.com/taishun/ 推荐查看天气:https://www.0dtq.com/taishun/zuotian.html 推荐查看天气:https://www.0dtq.com/taishun/7tian.html 推荐查看天气:https://www.0dtq.com/taishun/10tian.html 推荐查看天气:https://www.0dtq.com/taishun/15tian.html 推荐查看天气:https://www.0dtq.com/taishun/40tian.html 推荐查看天气:https://www.0dtq.com/ruian/ 推荐查看天气:https://www.0dtq.com/ruian/zuotian.html 推荐查看天气:https://www.0dtq.com/ruian/7tian.html 推荐查看天气:https://www.0dtq.com/ruian/10tian.html 推荐查看天气:https://www.0dtq.com/ruian/15tian.html 推荐查看天气:https://www.0dtq.com/ruian/40tian.html 推荐查看天气:https://www.0dtq.com/leqing/ 推荐查看天气:https://www.0dtq.com/leqing/zuotian.html 推荐查看天气:https://www.0dtq.com/leqing/7tian.html 推荐查看天气:https://www.0dtq.com/leqing/10tian.html 推荐查看天气:https://www.0dtq.com/leqing/15tian.html 推荐查看天气:https://www.0dtq.com/leqing/40tian.html 推荐查看天气:https://www.0dtq.com/jiaxing/ 推荐查看天气:https://www.0dtq.com/jiaxing/zuotian.html 推荐查看天气:https://www.0dtq.com/jiaxing/7tian.html 推荐查看天气:https://www.0dtq.com/jiaxing/10tian.html 推荐查看天气:https://www.0dtq.com/jiaxing/15tian.html 推荐查看天气:https://www.0dtq.com/jiaxing/40tian.html 推荐查看天气:https://www.0dtq.com/xiuzhou/ 推荐查看天气:https://www.0dtq.com/xiuzhou/zuotian.html 推荐查看天气:https://www.0dtq.com/xiuzhou/7tian.html 推荐查看天气:https://www.0dtq.com/xiuzhou/10tian.html 推荐查看天气:https://www.0dtq.com/xiuzhou/15tian.html 推荐查看天气:https://www.0dtq.com/xiuzhou/40tian.html 推荐查看天气:https://www.0dtq.com/jiashan/ 推荐查看天气:https://www.0dtq.com/jiashan/zuotian.html 推荐查看天气:https://www.0dtq.com/jiashan/7tian.html 推荐查看天气:https://www.0dtq.com/jiashan/10tian.html 推荐查看天气:https://www.0dtq.com/jiashan/15tian.html 推荐查看天气:https://www.0dtq.com/jiashan/40tian.html 推荐查看天气:https://www.0dtq.com/jiaxinghaiyan/ 推荐查看天气:https://www.0dtq.com/jiaxinghaiyan/zuotian.html 推荐查看天气:https://www.0dtq.com/jiaxinghaiyan/7tian.html 推荐查看天气:https://www.0dtq.com/jiaxinghaiyan/10tian.html 推荐查看天气:https://www.0dtq.com/jiaxinghaiyan/15tian.html 推荐查看天气:https://www.0dtq.com/jiaxinghaiyan/40tian.html 推荐查看天气:https://www.0dtq.com/haining/ 推荐查看天气:https://www.0dtq.com/haining/zuotian.html 推荐查看天气:https://www.0dtq.com/haining/7tian.html 推荐查看天气:https://www.0dtq.com/haining/10tian.html 推荐查看天气:https://www.0dtq.com/haining/15tian.html 推荐查看天气:https://www.0dtq.com/haining/40tian.html 推荐查看天气:https://www.0dtq.com/pinghu/ 推荐查看天气:https://www.0dtq.com/pinghu/zuotian.html 推荐查看天气:https://www.0dtq.com/pinghu/7tian.html 推荐查看天气:https://www.0dtq.com/pinghu/10tian.html 推荐查看天气:https://www.0dtq.com/pinghu/15tian.html 推荐查看天气:https://www.0dtq.com/pinghu/40tian.html 推荐查看天气:https://www.0dtq.com/tongxiang/ 推荐查看天气:https://www.0dtq.com/tongxiang/zuotian.html 推荐查看天气:https://www.0dtq.com/tongxiang/7tian.html 推荐查看天气:https://www.0dtq.com/tongxiang/10tian.html 推荐查看天气:https://www.0dtq.com/tongxiang/15tian.html 推荐查看天气:https://www.0dtq.com/tongxiang/40tian.html 推荐查看天气:https://www.0dtq.com/huzhou/ 推荐查看天气:https://www.0dtq.com/huzhou/zuotian.html 推荐查看天气:https://www.0dtq.com/huzhou/7tian.html 推荐查看天气:https://www.0dtq.com/huzhou/10tian.html 推荐查看天气:https://www.0dtq.com/huzhou/15tian.html 推荐查看天气:https://www.0dtq.com/huzhou/40tian.html 推荐查看天气:https://www.0dtq.com/wuxing/ 推荐查看天气:https://www.0dtq.com/wuxing/zuotian.html 推荐查看天气:https://www.0dtq.com/wuxing/7tian.html 推荐查看天气:https://www.0dtq.com/wuxing/10tian.html 推荐查看天气:https://www.0dtq.com/wuxing/15tian.html 推荐查看天气:https://www.0dtq.com/wuxing/40tian.html 推荐查看天气:https://www.0dtq.com/nanxun/ 推荐查看天气:https://www.0dtq.com/nanxun/zuotian.html 推荐查看天气:https://www.0dtq.com/nanxun/7tian.html 推荐查看天气:https://www.0dtq.com/nanxun/10tian.html 推荐查看天气:https://www.0dtq.com/nanxun/15tian.html 推荐查看天气:https://www.0dtq.com/nanxun/40tian.html 推荐查看天气:https://www.0dtq.com/deqingxian/ 推荐查看天气:https://www.0dtq.com/deqingxian/zuotian.html 推荐查看天气:https://www.0dtq.com/deqingxian/7tian.html 推荐查看天气:https://www.0dtq.com/deqingxian/10tian.html 推荐查看天气:https://www.0dtq.com/deqingxian/15tian.html 推荐查看天气:https://www.0dtq.com/deqingxian/40tian.html 推荐查看天气:https://www.0dtq.com/changxing/ 推荐查看天气:https://www.0dtq.com/changxing/zuotian.html 推荐查看天气:https://www.0dtq.com/changxing/7tian.html 推荐查看天气:https://www.0dtq.com/changxing/10tian.html 推荐查看天气:https://www.0dtq.com/changxing/15tian.html 推荐查看天气:https://www.0dtq.com/changxing/40tian.html 推荐查看天气:https://www.0dtq.com/anji/ 推荐查看天气:https://www.0dtq.com/anji/zuotian.html 推荐查看天气:https://www.0dtq.com/anji/7tian.html 推荐查看天气:https://www.0dtq.com/anji/10tian.html 推荐查看天气:https://www.0dtq.com/anji/15tian.html 推荐查看天气:https://www.0dtq.com/anji/40tian.html 推荐查看天气:https://www.0dtq.com/shaoxing/ 推荐查看天气:https://www.0dtq.com/shaoxing/zuotian.html 推荐查看天气:https://www.0dtq.com/shaoxing/7tian.html 推荐查看天气:https://www.0dtq.com/shaoxing/10tian.html 推荐查看天气:https://www.0dtq.com/shaoxing/15tian.html 推荐查看天气:https://www.0dtq.com/shaoxing/40tian.html 推荐查看天气:https://www.0dtq.com/yuecheng/ 推荐查看天气:https://www.0dtq.com/yuecheng/zuotian.html 推荐查看天气:https://www.0dtq.com/yuecheng/7tian.html 推荐查看天气:https://www.0dtq.com/yuecheng/10tian.html 推荐查看天气:https://www.0dtq.com/yuecheng/15tian.html 推荐查看天气:https://www.0dtq.com/yuecheng/40tian.html 推荐查看天气:https://www.0dtq.com/xinchang/ 推荐查看天气:https://www.0dtq.com/xinchang/zuotian.html 推荐查看天气:https://www.0dtq.com/xinchang/7tian.html 推荐查看天气:https://www.0dtq.com/xinchang/10tian.html 推荐查看天气:https://www.0dtq.com/xinchang/15tian.html 推荐查看天气:https://www.0dtq.com/xinchang/40tian.html 推荐查看天气:https://www.0dtq.com/zhuji/ 推荐查看天气:https://www.0dtq.com/zhuji/zuotian.html 推荐查看天气:https://www.0dtq.com/zhuji/7tian.html 推荐查看天气:https://www.0dtq.com/zhuji/10tian.html 推荐查看天气:https://www.0dtq.com/zhuji/15tian.html 推荐查看天气:https://www.0dtq.com/zhuji/40tian.html 推荐查看天气:https://www.0dtq.com/shangyu/ 推荐查看天气:https://www.0dtq.com/shangyu/zuotian.html 推荐查看天气:https://www.0dtq.com/shangyu/7tian.html 推荐查看天气:https://www.0dtq.com/shangyu/10tian.html 推荐查看天气:https://www.0dtq.com/shangyu/15tian.html 推荐查看天气:https://www.0dtq.com/shangyu/40tian.html 推荐查看天气:https://www.0dtq.com/chengzhou/ 推荐查看天气:https://www.0dtq.com/chengzhou/zuotian.html 推荐查看天气:https://www.0dtq.com/chengzhou/7tian.html 推荐查看天气:https://www.0dtq.com/chengzhou/10tian.html 推荐查看天气:https://www.0dtq.com/chengzhou/15tian.html 推荐查看天气:https://www.0dtq.com/chengzhou/40tian.html 推荐查看天气:https://www.0dtq.com/jinhua/ 推荐查看天气:https://www.0dtq.com/jinhua/zuotian.html 推荐查看天气:https://www.0dtq.com/jinhua/7tian.html 推荐查看天气:https://www.0dtq.com/jinhua/10tian.html 推荐查看天气:https://www.0dtq.com/jinhua/15tian.html 推荐查看天气:https://www.0dtq.com/jinhua/40tian.html 推荐查看天气:https://www.0dtq.com/jindong/ 推荐查看天气:https://www.0dtq.com/jindong/zuotian.html 推荐查看天气:https://www.0dtq.com/jindong/7tian.html 推荐查看天气:https://www.0dtq.com/jindong/10tian.html 推荐查看天气:https://www.0dtq.com/jindong/15tian.html 推荐查看天气:https://www.0dtq.com/jindong/40tian.html 推荐查看天气:https://www.0dtq.com/wuyi/ 推荐查看天气:https://www.0dtq.com/wuyi/zuotian.html 推荐查看天气:https://www.0dtq.com/wuyi/7tian.html 推荐查看天气:https://www.0dtq.com/wuyi/10tian.html 推荐查看天气:https://www.0dtq.com/wuyi/15tian.html 推荐查看天气:https://www.0dtq.com/wuyi/40tian.html 推荐查看天气:https://www.0dtq.com/jinhuapujiang/ 推荐查看天气:https://www.0dtq.com/jinhuapujiang/zuotian.html 推荐查看天气:https://www.0dtq.com/jinhuapujiang/7tian.html 推荐查看天气:https://www.0dtq.com/jinhuapujiang/10tian.html 推荐查看天气:https://www.0dtq.com/jinhuapujiang/15tian.html 推荐查看天气:https://www.0dtq.com/jinhuapujiang/40tian.html 推荐查看天气:https://www.0dtq.com/panan/ 推荐查看天气:https://www.0dtq.com/panan/zuotian.html 推荐查看天气:https://www.0dtq.com/panan/7tian.html 推荐查看天气:https://www.0dtq.com/panan/10tian.html 推荐查看天气:https://www.0dtq.com/panan/15tian.html 推荐查看天气:https://www.0dtq.com/panan/40tian.html 推荐查看天气:https://www.0dtq.com/lanxi/ 推荐查看天气:https://www.0dtq.com/lanxi/zuotian.html 推荐查看天气:https://www.0dtq.com/lanxi/7tian.html 推荐查看天气:https://www.0dtq.com/lanxi/10tian.html 推荐查看天气:https://www.0dtq.com/lanxi/15tian.html 推荐查看天气:https://www.0dtq.com/lanxi/40tian.html 推荐查看天气:https://www.0dtq.com/yiwu/ 推荐查看天气:https://www.0dtq.com/yiwu/zuotian.html

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册