文章目录
一、变量与作用域问题
1. 变量提升导致意外行为
- 场景:
console.log(x); // undefined var x = 10;
- 原因:
var
声明的变量会提升到作用域顶部,但赋值不会提升。 - 解决方案:
// 使用 let 或 const 代替 var let x = 10; console.log(x); // 10
2. 块级作用域失效
- 场景:
for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // 输出 3, 3, 3 }
- 原因:
var
没有块级作用域,i
是全局变量。 - 解决方案:
// 使用 let 创建块级作用域 for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // 输出 0, 1, 2 }
二、类型与比较问题
3. 隐式类型转换
- 场景:
console.log(1 + '1'); // '11' console.log('1' == 1); // true
- 原因:JavaScript 会自动进行类型转换。
- 解决方案:
// 使用严格相等 === console.log('1' === 1); // false
4. NaN 的奇怪行为
- 场景:
console.log(NaN === NaN); // false console.log(isNaN('foo')); // true
- 原因:
NaN
是唯一一个不等于自身的值,isNaN
会尝试将参数转换为数字。 - 解决方案:
// 使用 Number.isNaN console.log(Number.isNaN('foo')); // false
三、函数与闭包问题
5. this 指向错误
- 场景:
const obj = { name: 'Alice', greet: function() { console.log(`Hello, ${this.name}`); } }; setTimeout(obj.greet, 100); // Hello, undefined
- 原因:
this
在函数调用时动态绑定,setTimeout
中this
指向全局对象。 - 解决方案:
// 使用箭头函数或 bind setTimeout(() => obj.greet(), 100); // Hello, Alice setTimeout(obj.greet.bind(obj), 100); // Hello, Alice
6. 闭包内存泄漏
- 场景:
function createClosure() { const data = new Array(1000000).fill('data'); return function() { console.log(data.length); }; } const closure = createClosure();
- 原因:闭包会保留对外部变量的引用,导致内存无法释放。
- 解决方案:
// 手动释放引用 closure = null;
四、异步与 Promise 问题
7. 回调地狱
- 场景:
fetchData1(function(result1) { fetchData2(result1, function(result2) { fetchData3(result2, function(result3) { // ... }); }); });
- 原因:嵌套回调导致代码难以维护。
- 解决方案:
// 使用 Promise 或 async/await async function fetchData() { const result1 = await fetchData1(); const result2 = await fetchData2(result1); const result3 = await fetchData3(result2); // ... }
8. 未捕获的 Promise 错误
- 场景:
fetchData().then(result => { console.log(result); });
- 原因:未处理 Promise 的
reject
状态。 - 解决方案:
// 使用 catch 捕获错误 fetchData() .then(result => console.log(result)) .catch(error => console.error(error));
五、数组与对象问题
9. 数组遍历中的删除问题
- 场景:
const arr = [1, 2, 3, 4]; for (let i = 0; i < arr.length; i++) { if (arr[i] === 2) { arr.splice(i, 1); } } console.log(arr); // [1, 3, 4]
- 原因:删除元素后数组长度变化,导致跳过元素。
- 解决方案:
// 从后向前遍历 for (let i = arr.length - 1; i >= 0; i--) { if (arr[i] === 2) { arr.splice(i, 1); } }
10. 浅拷贝与深拷贝
- 场景:
const obj = { a: 1, b: { c: 2 } }; const shallowCopy = { ...obj }; shallowCopy.b.c = 3; console.log(obj.b.c); // 3
- 原因:扩展运算符 (
...
) 只能实现浅拷贝。 - 解决方案:
// 使用深拷贝 const deepCopy = JSON.parse(JSON.stringify(obj));
六、DOM 操作问题
11. 事件绑定重复
- 场景:
button.addEventListener('click', handleClick); button.addEventListener('click', handleClick);
- 原因:多次绑定同一事件处理函数。
- 解决方案:
// 先移除再绑定 button.removeEventListener('click', handleClick); button.addEventListener('click', handleClick);
12. DOM 操作性能问题
- 场景:
for (let i = 0; i < 1000; i++) { document.body.appendChild(document.createElement('div')); }
- 原因:频繁操作 DOM 导致性能问题。
- 解决方案:
// 使用文档片段 const fragment = document.createDocumentFragment(); for (let i = 0; i < 1000; i++) { fragment.appendChild(document.createElement('div')); } document.body.appendChild(fragment);
七、模块与作用域问题
13. 全局变量污染
- 场景:
var x = 10; // 全局变量
- 原因:未使用模块化或闭包隔离变量。
- 解决方案:
// 使用 IIFE 或模块化 (function() { var x = 10; // 局部变量 })();
14. 循环依赖
- 场景:
// a.js import { b } from './b'; export const a = b + 1; // b.js import { a } from './a'; export const b = a + 1;
- 原因:模块之间相互依赖导致死循环。
- 解决方案:
// 重构代码,避免循环依赖
八、其他常见错误
15. 未定义的变量
- 场景:
console.log(x); // ReferenceError: x is not defined
- 原因:变量未声明或拼写错误。
- 解决方案:
// 检查变量声明 let x = 10; console.log(x);
16. 意外的分号插入
- 场景:
return { name: 'Alice' };
- 原因:JavaScript 自动插入分号,导致返回
undefined
。 - 解决方案:
return { name: 'Alice' };
总结
通过理解 JavaScript 的核心概念(如作用域、闭包、异步等),并结合调试工具和代码审查,可以快速定位并解决问题。