From 91d1cbce10f6869c4282ff9a995d58671d3756d5 Mon Sep 17 00:00:00 2001 From: LCJ-MinYa <1049468118@qq.com> Date: Fri, 26 Dec 2025 15:24:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=20=20=20=20=20=20=20=20title:=20'?= =?UTF-8?q?=E8=A7=A3=E6=9E=84=E8=B5=8B=E5=80=BC=E4=B8=AD=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=80=BC=E4=B8=8D=E6=88=90=E5=8A=9F=EF=BC=8C?= =?UTF-8?q?=E5=8E=9F=E5=80=BC=E4=B8=BAnull=E7=9A=84=E9=97=AE=E9=A2=98',=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20title:=20'=E6=98=8E=E6=98=8E=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E6=8A=A5=E9=94=99=EF=BC=8C=E4=BD=86=E6=98=AF=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=E5=8F=B0=E4=B8=8D=E6=89=93=E5=8D=B0=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E5=87=A0=E7=A7=8D=E6=83=85=E5=86=B5=EF=BC=88=E5=88=AB?= =?UTF-8?q?=E6=80=80=E7=96=91=EF=BC=8C=E4=B8=80=E5=AE=9A=E6=98=AF=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=97=AE=E9=A2=98=EF=BC=8C=E8=80=8C=E4=B8=8D=E6=98=AF?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E8=BF=90=E8=A1=8C=E5=A4=AA=E4=B9=85=EF=BC=89?= =?UTF-8?q?',?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/modules/demo.ts | 8 + src/views/demo/catchError/catchError.md | 263 ++++++++++++++++++++++++ src/views/demo/catchError/index.vue | 18 ++ src/views/demo/dsAssign/dsAssign.md | 126 ++++++++++++ src/views/demo/dsAssign/index.vue | 18 ++ 5 files changed, 433 insertions(+) create mode 100644 src/views/demo/catchError/catchError.md create mode 100644 src/views/demo/catchError/index.vue create mode 100644 src/views/demo/dsAssign/dsAssign.md create mode 100644 src/views/demo/dsAssign/index.vue diff --git a/src/router/modules/demo.ts b/src/router/modules/demo.ts index fd11d8e..b659245 100644 --- a/src/router/modules/demo.ts +++ b/src/router/modules/demo.ts @@ -154,6 +154,14 @@ const titleArr = [ key: 'computedWithEnumList', title: '接口返回数据需要根据多个枚举值动态计算结果渲染(多个枚举值是接口返回,并且不知道具体返回时间)', }, + { + key: 'dsAssign', + title: '解构赋值中设置初始值不成功,原值为null的问题', + }, + { + key: 'catchError', + title: '明明程序报错,但是控制台不打印错误的几种情况(别怀疑,一定是代码问题,而不是程序运行太久)', + }, ]; // @/views/demo/**/*.vue diff --git a/src/views/demo/catchError/catchError.md b/src/views/demo/catchError/catchError.md new file mode 100644 index 0000000..d75a7ad --- /dev/null +++ b/src/views/demo/catchError/catchError.md @@ -0,0 +1,263 @@ +# 明明程序报错,但是控制台不打印错误的几种情况(别怀疑,一定是代码问题,而不是程序运行太久) + +这是一个很常见但容易让人困惑的问题。以下是程序报错但控制台不打印错误的几种常见情况: + +## 1. **异步错误未被捕获** + +### Promise 错误 +```javascript +// ❌ 错误不会打印到控制台 +new Promise((resolve, reject) => { + throw new Error('Promise error'); +}); + +// ✅ 需要 catch 处理 +new Promise((resolve, reject) => { + throw new Error('Promise error'); +}).catch(error => { + console.error('Caught:', error); +}); +``` + +### async/await 未捕获 +```javascript +// ❌ 错误不会打印 +async function test() { + throw new Error('Async error'); +} +test(); + +// ✅ 需要 try-catch 或外层 catch +async function test() { + throw new Error('Async error'); +} +test().catch(error => console.error('Caught:', error)); +``` + +## 2. **setTimeout/setInterval 中的错误** +```javascript +// ❌ 错误不会传播到外层 +setTimeout(() => { + throw new Error('Timeout error'); +}, 0); + +// ✅ 需要在内部 try-catch +setTimeout(() => { + try { + throw new Error('Timeout error'); + } catch (error) { + console.error('Caught in timeout:', error); + } +}, 0); +``` + +## 3. **事件监听器中的错误** +```javascript +// ❌ 错误被事件系统吞掉 +element.addEventListener('click', () => { + throw new Error('Event handler error'); +}); + +// ✅ 需要内部 try-catch +element.addEventListener('click', () => { + try { + throw new Error('Event handler error'); + } catch (error) { + console.error('Caught in event:', error); + } +}); +``` + +## 4. **微任务队列中的错误** +```javascript +// ❌ queueMicrotask 中的错误 +queueMicrotask(() => { + throw new Error('Microtask error'); +}); + +// ✅ 需要包装处理 +queueMicrotask(() => { + try { + throw new Error('Microtask error'); + } catch (error) { + console.error('Caught in microtask:', error); + } +}); +``` + +## 5. **Web Workers 中的错误** +```javascript +// worker.js +// ❌ 错误不会自动传递到主线程 +throw new Error('Worker error'); + +// ✅ 需要通过 onerror 或 postMessage +self.onerror = function(error) { + console.error('Worker error:', error); + return false; // 返回 false 让错误冒泡到主线程 +}; +``` + +## 6. **模块加载错误** +```javascript +// ❌ ES6 模块中的顶级错误可能被静默处理 +import './module-that-throws.js'; // 模块内部直接 throw + +// ✅ 使用动态导入捕获 +import('./module-that-throws.js') + .catch(error => console.error('Module load error:', error)); +``` + +## 7. **被 try-catch 吞掉的错误** +```javascript +// ❌ 错误被捕获但没有处理 +try { + throw new Error('Some error'); +} catch (error) { + // 没有 console.error,错误就消失了 +} + +// ❌ 更隐蔽的:catch 块自身出错 +try { + throw new Error('First error'); +} catch (error) { + console.nonExistentMethod(); // 这个错误不会被捕获! +} +``` + +## 8. **跨域脚本错误** +```javascript +// ❌ 跨域脚本的错误信息受限 + + +// 浏览器出于安全考虑,只会显示 "Script error." +``` + +## 9. **Console 被重写** +```javascript +// ❌ 有人重写了 console.error +const originalError = console.error; +console.error = () => {}; // 静默所有错误 + +// 检查 console 是否被篡改 +if (console.error.toString().includes('[native code]')) { + console.log('Console is native'); +} else { + console.log('Console has been overwritten!'); +} +``` + +## 10. **错误在框架中被处理** +### React 的错误边界 +```jsx +// ❌ React 16+ 的错误被错误边界捕获 +class ErrorBoundary extends React.Component { + componentDidCatch(error, info) { + // 如果没有 console.error,错误不会显示 + // console.error('Error caught by boundary:', error); + } + + render() { + return this.props.children; + } +} +``` + +### Vue 的错误处理 +```javascript +// ❌ Vue 全局错误处理器 +Vue.config.errorHandler = function (err, vm, info) { + // 如果没有处理,错误不会显示 + // console.error('Vue error:', err); +}; +``` + +## 11. **Node.js 特定情况** +### 未监听的 'error' 事件 +```javascript +// ❌ EventEmitter 的 error 事件需要监听 +const stream = createReadStream('nonexistent.txt'); +// 如果没有 error 监听器,错误会抛出并崩溃进程 + +// ✅ 必须监听 error 事件 +stream.on('error', (error) => { + console.error('Stream error:', error); +}); +``` + +### Domain 模块(已废弃但可能遇到) +```javascript +const domain = require('domain'); +const d = domain.create(); + +// ❌ Domain 可能吞掉错误 +d.on('error', (err) => { + // 如果没有处理,错误消失 +}); + +d.run(() => { + throw new Error('Domain error'); +}); +``` + +## 调试和诊断技巧 + +### 1. **全局错误监听器** +```javascript +// 捕获未处理的 Promise 错误 +window.addEventListener('unhandledrejection', event => { + console.error('Unhandled promise rejection:', event.reason); + event.preventDefault(); // 阻止默认错误输出 +}); + +// 捕获全局错误 +window.addEventListener('error', event => { + console.error('Global error:', event.error); +}); + +// Node.js +process.on('unhandledRejection', (reason, promise) => { + console.error('Unhandled Rejection at:', promise, 'reason:', reason); +}); + +process.on('uncaughtException', (error) => { + console.error('Uncaught Exception:', error); +}); +``` + +### 2. **启用所有错误显示** +```javascript +// Chrome DevTools +// 1. 打开 DevTools → Sources +// 2. 点击 Pause on exceptions 按钮(⏸️) +// 3. 勾选 "Pause on caught exceptions" + +// Node.js 调试 +node --inspect app.js +// 或使用 ndb、node --inspect-brk +``` + +### 3. **检查控制台过滤器** +有时控制台过滤器可能被设置为隐藏错误: +- 检查 Console 顶部的过滤选项 +- 确保 "Errors", "Warnings" 没有被过滤掉 +- 尝试点击 "Default levels" → "All levels" + +### 4. **最小化复现** +```javascript +// 创建一个最小测试 +console.log('Script is running'); +setTimeout(() => { + console.log('About to throw...'); + throw new Error('Test error'); +}, 1000); +``` + +## 总结 +当错误不显示时,通常是因为: +1. **异步上下文**中的错误 +2. **被某种机制捕获**但未处理 +3. **框架或环境**的特定错误处理 +4. **控制台配置**问题 + +最好的做法是始终添加全局错误监听器,并在开发时启用"捕获异常时暂停"功能。 \ No newline at end of file diff --git a/src/views/demo/catchError/index.vue b/src/views/demo/catchError/index.vue new file mode 100644 index 0000000..e0f5eac --- /dev/null +++ b/src/views/demo/catchError/index.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/views/demo/dsAssign/dsAssign.md b/src/views/demo/dsAssign/dsAssign.md new file mode 100644 index 0000000..94f0ba2 --- /dev/null +++ b/src/views/demo/dsAssign/dsAssign.md @@ -0,0 +1,126 @@ +# js 解构赋值中设置初始值不成功,原值为null的问题 +在 JavaScript 解构赋值中,当原值为 `null` 时,设置默认值(初始值)可能会失败。这是因为 `null` 被认为是一个有效的值(虽然为空),解构赋值不会将其视为 `undefined`。 + +## 问题示例 + +```javascript +// 正常情况:undefined 会触发默认值 +const obj1 = { a: undefined, b: 2 }; +const { a = 1, b = 1 } = obj1; +console.log(a); // 1 ✅ 默认值生效 +console.log(b); // 2 + +// 问题情况:null 不会触发默认值 +const obj2 = { a: null, b: 2 }; +const { a = 1, b = 1 } = obj2; +console.log(a); // null ❌ 默认值不生效 +console.log(b); // 2 +``` + +## 解决方案 + +### 1. **使用逻辑或运算符(||)** +```javascript +const obj = { a: null, b: 2 }; +const { a, b } = obj; +const aWithDefault = a || 1; // 如果 a 是 null、undefined、0、""、false 等,使用默认值 +console.log(aWithDefault); // 1 +``` + +### 2. **使用空值合并运算符(??)(ES2020)** +```javascript +const obj = { a: null, b: 2 }; +const { a, b } = obj; +const aWithDefault = a ?? 1; // 只在 a 是 null 或 undefined 时使用默认值 +console.log(aWithDefault); // 1 +``` + +### 3. **解构后处理** +```javascript +const obj = { a: null, b: 2 }; +let { a, b } = obj; // !!!!!请注意下面如果要重新设置a的值,必须使用let解构,如果const会报错 +a = a === null ? 1 : a; +console.log(a); // 1 +``` + +### 4. **使用函数包装** +```javascript +function destructureWithDefaults(obj, defaults) { + const result = { ...defaults }; + for (const key in obj) { + if (obj[key] !== undefined) { + result[key] = obj[key]; + } + } + return result; +} + +const obj = { a: null, b: 2 }; +const { a, b } = destructureWithDefaults(obj, { a: 1, b: 1 }); +console.log(a); // null +console.log(b); // 2 +``` + +### 5. **处理嵌套解构** +```javascript +const obj = { + user: null, + settings: { theme: 'dark' } +}; + +// 嵌套解构也需要处理 null +const { + user = {}, // 默认值不会生效,因为 user 是 null + settings = {} +} = obj || {}; // 先确保 obj 不是 null + +// 更好的方式 +const safeObj = obj || {}; +const safeUser = safeObj.user || {}; +const safeSettings = safeObj.settings || {}; +``` + +### 6. **自定义解构辅助函数** +```javascript +function safeDestructure(obj, defaults) { + return Object.keys(defaults).reduce((acc, key) => { + const value = obj[key]; + acc[key] = value === null || value === undefined ? defaults[key] : value; + return acc; + }, {}); +} + +const obj = { a: null, b: 2, c: undefined }; +const { a, b, c } = safeDestructure(obj, { a: 1, b: 1, c: 3, d: 4 }); +console.log(a); // 1 +console.log(b); // 2 +console.log(c); // 3 +``` + +### 7. **使用 TypeScript(编译时检查)** +```typescript +interface MyObject { + a: number | null; + b: number; +} + +const obj: MyObject = { a: null, b: 2 }; +const { a = 1, b = 1 } = obj; // TypeScript 会警告可能的问题 +// 需要显式处理 +const safeA = a ?? 1; +``` + +## 最佳实践建议 + +1. **了解数据来源**:如果数据可能包含 `null`,提前处理 +2. **使用空值合并运算符(??)**:ES2020+ 环境的最佳选择 +3. **数据清洗**:在解构前将 `null` 转换为 `undefined` + ```javascript + const cleanObj = Object.fromEntries( + Object.entries(obj).map(([k, v]) => [k, v === null ? undefined : v]) + ); + const { a = 1 } = cleanObj; + ``` +4. **防御性编程**:对可能为 `null` 的层级都进行保护 + +记住:解构赋值的默认值只在属性值为 `undefined` 时生效,`null` 是一个明确的值,不会触发默认值。 \ No newline at end of file diff --git a/src/views/demo/dsAssign/index.vue b/src/views/demo/dsAssign/index.vue new file mode 100644 index 0000000..db485f8 --- /dev/null +++ b/src/views/demo/dsAssign/index.vue @@ -0,0 +1,18 @@ + + +