0

0

代码运行慢?避免这19个常见的JavaScript和Node.js错误,让你的程序高速狂飙

DDD

DDD

发布时间:2023-09-22 10:50:48

|

1791人浏览过

|

来源于php中文网

原创

缓慢或卡顿的网站是业余爱好者的标志,而流畅、优化的体验会让用户感到高兴,并使专业人士脱颖而出。

但创建真正高性能的网络应用程序充满了陷阱。错误比比皆是,它们可能会拖慢JavaScript的速度,而您甚至没有意识到这一点。微小的疏忽会让你的代码变得臃肿,并悄悄地一点一点地降低速度。

这是怎么回事?

事实证明,我们有很多常见的方式会无意中减慢 JavaScript 的速度。随着时间的推移,可能会阻碍网站性能。

这些错误是可以避免的。

立即学习Java免费学习笔记(深入)”;

今天,我们重点关注可能会悄悄减慢 JavaScript 和 Node.js 应用程序速度的 19 个性能陷阱。我们将通过说明性示例和可操作的解决方案来探索导致这些问题的原因,以优化您的代码。

识别并消除这些危害是打造让用户满意的流畅网络体验的关键。那么,让我们深入了解一下吧!

1. 不正确的变量声明和作用域

第一次学习 JavaScript 时,很容易在全局声明所有变量。然而,这会导致未来出现问题。让我们看一个例子:

// globals.js
var color = 'blue';
function printColor() {
  console.log(color); 
}
printColor(); // Prints 'blue'

这工作正常,但想象一下如果我们加载另一个脚本:

// script2.js
var color = 'red';
printColor(); // Prints 'red'!

因为color是全局的,所以script2.js覆盖了它!要解决此问题,请尽可能在函数内部声明变量:

function printColor() {
  var color = 'blue'; // local variable
  
  console.log(color);
}
printColor(); // Prints 'blue'

现在,其他脚本的更改不会影响printColor.

不必要时在全局范围内声明变量是一种反模式。尝试将全局变量限制为配置常量。对于其他变量,请在尽可能小的范围内进行本地声明。

2. 低效的 DOM 操作

更新 DOM 元素时,批量更改而不是一次操作一个节点。考虑这个例子:

const ul = document.getElementById('list');
for (let i = 0; i < 10; i++) {
  const li = document.createElement('li');
  li.textContent = i;
  
  ul.appendChild(li);
}

这将逐一附加列表项。最好先构建一个字符串然后设置.innerHTML:

const ul = document.getElementById('list');
let html = '';
for (let i = 0; i < 10; i++) {
  html += `
  • ${i}
  • `; } ul.innerHTML = html;

    构建字符串可以最大限度地减少回流。我们更新 DOM 一次而不是 10 次。

    对于多个更新,构建更改,然后在最后应用。或者更好的是,使用 DocumentFragment 批量追加。

    3. 过多的 DOM 操作

    频繁的DOM更新会降低性能。考虑一个将消息插入页面的聊天应用程序。

    反面例子:

    // New message received
    const msg = `
    ${messageText}
    `; chatLog.insertAdjacentHTML('beforeend', msg);

    这天真地插入到每条消息上。最好是限制更新:

    正确示例:

    let chatLogHTML = '';
    const throttleTime = 100; // ms
    // New message received  
    chatLogHTML += `
    ${messageText}
    `; // Throttle DOM updates setTimeout(() => { chatLog.innerHTML = chatLogHTML; chatLogHTML = ''; }, throttleTime);

    现在,我们最多每 100 毫秒更新一次,从而保持 DOM 操作较低。

    对于高度动态的 UI,请考虑像 React 这样的虚拟 DOM 库。这些最大限度地减少了使用虚拟表示的 DOM 操作。

    4.缺乏活动委托

    将事件侦听器附加到许多元素会产生不必要的开销。考虑一个每行都有删除按钮的表:

    反面例子:

    const rows = document.querySelectorAll('table tr');
    rows.forEach(row => {
      const deleteBtn = row.querySelector('.delete');  
      deleteBtn.addEventListener('click', handleDelete);
    });

    这会为每个删除按钮添加一个侦听器。需要更好地使用事件委托:

    正确示例:

    const table = document.querySelector('table');
    table.addEventListener('click', e => {
      if (e.target.classList.contains('delete')) {
        handleDelete(e);
      }
    });

    现在,.net 上只有一个侦听器,更少的内存开销。

    事件委托利用事件冒泡。一个侦听器可以处理来自多个后代的事件。只要适用,就使用委派。

    5. 低效的字符串连接

    在循环中连接字符串时,性能会受到影响。考虑这段代码:

    let html = '';
    for (let i = 0; i < 10; i++) {
      html += '
    ' + i + '
    '; }

    创建新字符串需要分配内存。最好使用数组:

    const parts = [];
    for (let i = 0; i < 10; i++) {
      parts.push('
    ', i, '
    '); } const html = parts.join('');

    构建数组可以最大限度地减少中间字符串。.join()最后连接一次。

    对于多个字符串添加,请使用数组连接。另外,请考虑嵌入值的模板文字。

    6. 未优化的循环

    JavaScript 中的循环经常会导致性能问题。一个常见的错误是重复访问数组长度:

    反面例子:

    const items = [/*...*/];
    for (let i = 0; i < items.length; i++) {
      // ...
    }

    冗余检查.length会抑制优化。

    正确示例:

    const items = [/*...*/];  
    const len = items.length;
    for (let i = 0; i < len; i++) {
      // ...
    }

    缓存长度可以提高速度。其他优化包括将不变量提升到循环之外、简化终止条件以及避免迭代内昂贵的操作。

    7. 不必要的同步操作

    JavaScript 的异步功能是一个关键优势。但要小心阻塞 I/O!例如:

    反面例子:

    const data = fs.readFileSync('file.json'); // blocks!

    这会在从磁盘读取时停止执行。相反,如果使用回调或承诺:

    正确示例:

    fs.readFile('file.json', (err, data) => {
      // ...
    });

    现在,事件循环在读取文件时继续。对于复杂的流程,async/await简化异步逻辑。避免同步操作以防止阻塞。

    8. 阻止事件循环

    JavaScript 使用单线程事件循环。阻止它会停止执行。一些常见的拦截器:

    繁重的计算任务

    同步输入/输出

    未优化的算法

    例如:

    function countPrimes(max) {
      // Unoptimized loop
      for (let i = 0; i <= max; i++) {
        // ...check if prime...
      }
    }
    countPrimes(1000000); // Long running!

    这会同步执行,并阻止其他事件。避免:

    推迟不必要的工作

    批量数据处理

    使用工作线程

    寻找优化机会

    保持事件循环顺利运行。定期分析以捕获阻塞代码。

    9. 错误处理效率低下

    在 JavaScript 中正确处理错误至关重要。但要小心性能陷阱!

    反面例子:

    try {
      // ...
    } catch (err) {
      console.error(err); // just logging
    }

    这会捕获错误但不采取纠正措施。未处理的错误通常会导致内存泄漏或数据损坏。

    正确示例:

    try {
      // ...
    } catch (err) {
      console.error(err);
      
      // Emit error event 
      emitError(err); 
      
      // Nullify variables
      obj = null;
      
      // Inform user
      showErrorNotice();
    }

    记录还不够!清理工件、通知用户并考虑恢复选项。使用 Sentry 等工具来监控生产中的错误。明确处理所有错误。

    10. 内存泄漏

    当内存被分配但从未释放时,就会发生内存泄漏。随着时间的推移,泄漏会累积并降低性能。

    JavaScript 中的常见来源包括:

    未清理的事件监听器

    ChartGen
    ChartGen

    AI快速生成专业数据图表

    下载

    对已删除 DOM 节点的过时引用

    不再需要的缓存数据

    闭包中的累积状态

    例如:

    function processData() {
      const data = [];
      // Use closure to accumulate data
      return function() {
        data.push(getData()); 
      }
    }
    const processor = processData();
    // Long running...keeps holding reference to growing data array!

    数组不断变大,但从未被清除。修理:

    使用弱引用

    清理事件监听器

    删除不再需要的引用

    限制关闭状态大小

    监视内存使用情况并观察增长趋势。在泄漏堆积之前主动消除泄漏。

    11. 过度使用依赖项

    虽然 npm 提供了无穷无尽的选择,但请抵制过度导入的冲动!每个依赖项都会增加包大小和攻击面。

    反面例子:

    import _ from 'lodash';
    import moment from 'moment'; 
    import validator from 'validator';
    // etc...

    为次要实用程序导入整个库。最好根据需要挑选助手:

    正确示例:

    import cloneDeep from 'lodash/cloneDeep';
    import { format } from 'date-fns';
    import { isEmail } from 'validator';

    只导入您需要的内容。定期检查依赖关系以删除未使用的依赖关系。保持捆绑精简并最大限度地减少依赖性。

    12. 缓存不足

    缓存允许通过重用先前的结果来跳过昂贵的计算。但它经常被忽视。

    反面例子:

    function generateReport() {
      // Perform expensive processing
      // to generate report data... 
    }
    generateReport(); // Computes
    generateReport(); // Computes again!

    由于输入没有更改,因此可以缓存报告:

    正确示例:

    let cachedReport;
    function generateReport() {
      if (cachedReport) {
        return cachedReport;
      }
      cachedReport = // expensive processing...
      return cachedReport; 
    }

    现在,重复调用速度很快。

    13. 未优化的数据库查询

    与数据库交互时,低效的查询可能会降低性能。需要避免的一些问题:

    反面例子:

    // No indexing
    db.find({name: 'John', age: 35}); 
    // Unecessary fields
    db.find({first: 'John', last:'Doe', email:'john@doe.com'}, {first: 1, last: 1});
    // Too many separate queries
    for (let id of ids) {
      const user = db.find({id});
    }

    这无法利用索引、检索未使用的字段并执行过多的查询。

    正确示例:

    // Use index on 'name' 
    db.find({name: 'John'}).hint({name: 1});
    // Only get 'email' field
    db.find({first: 'John'}, {email: 1}); 
    // Get users in one query
    const users = db.find({
      id: {$in: ids} 
    });

    分析并解释计划。战略性地创建索引。避免多次零散的查询。优化数据存储交互。

    14. Promise 中错误处理不当

    Promise 简化了异步代码。但未经处理的拒绝就是无声的失败!

    反面例子:

    function getUser() {
      return fetch('/user')
        .then(r => r.json()); 
    }
    getUser();

    如果fetch拒绝,异常就不会被注意到。

    正确示例:

    function getUser() {
      return fetch('/user')
        .then(r => r.json())
        .catch(err => console.error(err));
    } 
    getUser();

    链接.catch()可以正确处理错误。

    15. 同步网络操作

    网络请求应该是异步的。但有时会使用同步变体:

    反面例子:

    const data = http.getSync('http://example.com/data'); // blocks!

    这会在请求期间停止事件循环。相反,使用回调:

    正确示例:

    http.get('http://example.com/data', res => {
      // ...
    });

    或者:

    fetch('http://example.com/data')
      .then(res => res.json())
      .then(data => {
        // ...
      });

    异步网络请求允许在等待响应时进行其他处理。避免同步网络调用。

    16. 低效的文件 I/O 操作

    读/写文件同步阻塞。例如:

    反面例子:

    const contents = fs.readFileSync('file.txt'); // blocks!

    这会在磁盘 I/O 期间停止执行。

    正确示例:

    fs.readFile('file.txt', (err, contents) => {
      // ...
    });
    // or promises
    fs.promises.readFile('file.txt')
       .then(contents => {
         // ...  
       });

    这允许事件循环在文件读取期间继续。

    对于多个文件,使用流:

    function processFiles(files) {
      for (let file of files) {
        fs.createReadStream(file)
          .pipe(/*...*/);
      }
    }

    避免同步文件操作。使用回调、promise 和流。

    17. 忽略性能分析和优化

    在出现明显问题之前,很容易忽视性能。但优化应该持续进行!首先使用分析工具进行测量:

    浏览器开发工具时间线

    Node.js 分析器

    第三方分析器

    即使性能看起来不错,这也揭示了优化机会:

    // profile.js
    function processOrders(orders) {
      orders.forEach(o => {
        // ...
      });
    }
    processOrders(allOrders);

    分析器显示processOrders需要 200 毫秒。

    分析指导优化。制定绩效预算,如果超出则失败。经常测量并明智地优化。

    18. 不利用缓存机制

    缓存通过避免重复工作来提高速度。但它经常被遗忘。

    反面例子:

    // Compute expensive report
    function generateReport() {
      // ...heavy processing...
    }
    generateReport(); // Computes
    generateReport(); // Computes again!

    相同的输入总是产生相同的输出。我们应该缓存:

    正确示例:

    // Cache report contents
    const cache = {};
    function generateReport() {
      if (cache.report) {
        return cache.report;
      }
      const report = // ...compute...
      cache.report = report;
      return report;
    }

    现在,重复调用速度很快。

    19. 不必要的代码重复

    重复的代码会损害可维护性和可优化性。

    function userStats(user) {
      const name = user.name;
      const email = user.email;
      
      // ...logic...
    }
    function orderStats(order) {
      const name = order.customerName;
      const email = order.customerEmail;
      // ...logic... 
    }

    提取是重复的。我们重来:

    function getCustomerInfo(data) {
      return {
        name: data.name, 
        email: data.email
      };
    }
    function userStats(user) {
      const { name, email } = getCustomerInfo(user);
      
      // ...logic...
    }
    function orderStats(order) {
      const { name, email } = getCustomerInfo(order);
      // ...logic...
    }

    现在,它只定义一次。

    结论

    优化 JavaScript 应用程序性能是一个迭代过程。通过学习有效的实践并勤于分析,可以显着提高速度。

    需要关注的关键领域包括最大限度地减少 DOM 更改、利用异步技术、消除阻塞操作、减少依赖性、利用缓存以及删除不需要的重复。

    相关文章

    java速学教程(入门到精通)
    java速学教程(入门到精通)

    java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

    下载

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

    相关专题

    更多
    js获取数组长度的方法
    js获取数组长度的方法

    在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

    554

    2023.06.20

    js刷新当前页面
    js刷新当前页面

    js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

    374

    2023.07.04

    js四舍五入
    js四舍五入

    js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

    731

    2023.07.04

    js删除节点的方法
    js删除节点的方法

    js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

    477

    2023.09.01

    JavaScript转义字符
    JavaScript转义字符

    JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

    394

    2023.09.04

    js生成随机数的方法
    js生成随机数的方法

    js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

    990

    2023.09.04

    如何启用JavaScript
    如何启用JavaScript

    JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

    656

    2023.09.12

    Js中Symbol类详解
    Js中Symbol类详解

    javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

    551

    2023.09.20

    Golang gRPC 服务开发与Protobuf实战
    Golang gRPC 服务开发与Protobuf实战

    本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

    8

    2026.01.15

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    React 教程
    React 教程

    共58课时 | 3.7万人学习

    TypeScript 教程
    TypeScript 教程

    共19课时 | 2.2万人学习

    Bootstrap 5教程
    Bootstrap 5教程

    共46课时 | 2.9万人学习

    关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
    php中文网:公益在线php培训,帮助PHP学习者快速成长!
    关注服务号 技术交流群
    PHP中文网订阅号
    每天精选资源文章推送

    Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号