Bootstrap

JavaScript 开发中常见 Bug 合集及解决方案(超全总结)

在这里插入图片描述

一、变量与作用域问题

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 在函数调用时动态绑定,setTimeoutthis 指向全局对象。
  • 解决方案
    // 使用箭头函数或 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 的核心概念(如作用域、闭包、异步等),并结合调试工具和代码审查,可以快速定位并解决问题。

;