10个JavaScript性能优化实战技巧与基准测试

作者:白帽猫

时间:2026-10-25

浏览:332

本文通过benchmark.js工具进行实测,分享10个JavaScript代码优化技巧。涵盖全局变量慎用、原型方法定义、闭包内存释放、属性访问优化、循环缓存、循环方式选择、判断层级减少、作用域链优化及字面量声明等核心场景,提供具体代码对比与性能数据参考。

文章目录

前言

实现JavaScript代码优化的首要步骤,是准确测量代码的执行耗时。这通常需要采集大量执行样本进行数学统计与分析,本文采用benchmark.js库来检测代码的实际运行表现。

首先需要在项目中安装该依赖,具体安装命令如下:

yarn add benchmark --save
# 或者
npm i benchmark --save

随后编写测试脚本,示例代码如下:

const Benchmark = require('benchmark')
const suite = new Benchmark.Suite()
// 添加测试
suite
  /**
   * add() 方法接受两个参数,其中第一个表示测试的名称,第二个表示测试的内容,他是一个函数*   
/
  .add('join1000', () => {
    new Array(1000).join(' ')
  })
  .add('join10000', () => {
    new Array(10000).join(' ')
  })
  // 添加时间监听
  .on('cycle', event => {
    // 打印执行时间
    console.log(String(event.target))
  })
  // 完成后执行触发的事件
  .on('complete', () => {
    console.log('最快的是:' + suite.filter('fastest').map('name'))
  })
  // 执行测试
  .run({ async: true })

复制代码

代码执行后的输出结果如下:

// join1000 x 146,854 ops/sec ±1.86% (88 runs sampled) // join10000 x 16,083 ops/sec ±1.06% (92 runs sampled) // 最快的是:join1000

在输出结果中,ops/sec代表每秒执行的次数,数值越大表示性能越好;其后紧跟着的是执行次数上下波动的百分比;括号内的数值则表示总共进行了多少次采样。

此外,也可以选用JSBench.me在线工具进行对比测试,网站测试截图如下:

前言

从结果可见,join1000的性能表现更优(这似乎是显而易见的结论)。

慎用全局变量

之所以强调慎用全局变量,主要基于以下原因:

  • 全局变量定义在全局执行上下文中,处于作用域链的最顶端。每次查找变量时都需要从局部作用域一直查找到最顶层,这会消耗额外的时间。
  • 全局执行上下文会一直保留在调用栈中,直到程序完全退出才会被销毁,这会造成内存空间的浪费。
  • 如果局部作用域中定义了同名变量,可能会遮盖或污染全局变量,引发潜在问题。

接下来通过一段代码对比全局变量与局部变量在执行效率上的差异,代码如下:

...
suite
  .add('全局变量', () => {
    // 该函数内模拟全局作用域
    let i,
      str = ''
    for (i = 0; i < 1000; i++) {
      str += i
    }
  })
  .add('局部变量', () => {
    for (let i = 0, str = ''; i < 1000; i++) {
      str += i
    }
  })
...

代码运行结果如下:

全局变量 x 158,697 ops/sec ±1.05% (87 runs sampled) 局部变量 x 160,697 ops/sec ±1.03% (90 runs sampled) 最快的是:局部变量

虽然差异并不显著,但可以感知到全局变量的性能略逊于局部变量。

通过原型新增方法

在为构造函数增加实例对象所需的方法时,建议优先使用原型方式添加,而非在构造函数内部直接添加。我们可以参考以下测试代码:

...
suite
  .add('构造函数内部添加', () => {
    function Person() {
      this.sayMe = function () {
        return '一碗周'
      }
    }
    let p = new Person()
  })
  .add('原型方式内部添加', () => {
    function Person() {}
    Person.prototype.sayMe = function () {
      return '一碗周'
    }
    let p = new Person()
  })
...

代码运行结果如下:

构造函数内部添加 x 573,786 ops/sec ±1.97% (89 runs sampled) 原型方式内部添加 x 581,693 ops/sec ±3.46% (80 runs sampled) 最快的是:构造函数内部添加

避免闭包中的内存泄露

由于闭包机制会导致函数内的变量长期保存在内存中,若内存消耗过大且滥用闭包,可能引发网页性能问题,严重时甚至导致内存泄露。解决方法是在退出函数前,将不再使用的局部变量全部删除(即将局部变量重新赋值为null)。

避免使用属性访问方法

在JavaScript对象中,应尽量避免使用某些特定的属性访问方式,因为JavaScript中的所有属性默认都是外部可见的。

示例代码如下:

...
suite
  .add('使用属性访问方法', () => {
    function Person() {
      this.name = '一碗周'
      this.getName = function () {
        return '一碗周'
      }
    }
    let p = new Person()
    let n = p.getName()
  })
  .add('不使用属性访问方法', () => {
    function Person() {
      this.name = '一碗周'
    }
    let p = new Person()
    let n = p.name
  })
...

代码运行结果如下:

使用属性访问方法 x 406,682 ops/sec ±2.33% (82 runs sampled) 不使用属性访问方法 x 554,169 ops/sec ±2.03% (85 runs sampled) 最快的是:不使用属性访问方法

for循环优化

在使用for循环时,可以将某些必要的数据进行缓存,例如arr.length这类属性,无需在每次判断时都重新获取,从而优化代码性能。

示例代码如下:

...
suite
  .add('正序', () => {
    let arr = new Array(100)
    let str = ''
    for (let i = 0; i < arr.length; i++) {
      str += i
    }
  })
  .add('缓存', () => {
    let arr = new Array(100)
    let str = ''
    for (let i = arr.length; i; i--) {
      str += i
    }
  })
  .add('缓存的另一种写法', () => {
    let arr = new Array(100)
    let str = ''
    for (let i = 0, l = arr.length; i < l; i++) {
      str += i
    }
  })
...

代码运行结果如下:

正序 x 1,322,889 ops/sec ±1.36% (86 runs sampled) 缓存 x 1,356,696 ops/sec ±0.70% (92 runs sampled) 缓存的另一种写法 x 1,383,091 ops/sec ±0.70% (93 runs sampled) 最快的是:缓存的另一种写法

选择最优的循环方式

目前常用的循环方式包括forEach、for和for...in,哪一种性能最优?测试代码如下:

...
suite
  .add('forEach', () => {
    let arr = new Array(100)
    let str = ''
    arr.forEach(i => {
      str += i
    })
  })
  .add('for...in', () => {
    let arr = new Array(100)
    let str = ''
    for (i in arr) {
      str += i
    }
  })
  .add('for', () => {
    let arr = new Array(100)
    let str = ''
    for (let i = 0, l = arr.length; i < l; i++) {
      str += i
    }
  })
...

代码运行结果如下:

forEach x 4,248,577 ops/sec ±0.89% (86 runs sampled) for...in x 4,583,375 ops/sec ±1.15% (91 runs sampled) for x 1,343,871 ops/sec ±1.91% (88 runs sampled) 最快的是:for...in

由运行结果可以看出,建议尽量使用for...in或者forEach循环,并减少使用for循环。

减少判断层级

减少判断层级即减少if语句的嵌套。对于必要的条件判断,可以通过单层if结合return直接跳出函数执行。关于优化前与优化后的代码执行对比如下所示:

...
/*** 
 接收两类文件,zip 和 rar* 
 压缩包的大小限制为 10 兆* 
/

suite
  .add('嵌套写法', () => {
    function uploadFile(suffix, size) {
      // 允许上传的后缀名
      const suffixList = ['.zip', '.rar']
      const M = 1024*  1024

      if (suffixList.includes(suffix)) {
        if (size <= 10*  M) {
          return '下载成功'
        }
      }
    }
    uploadFile('.zip', 1*  1024*  1024)
  })
  .add('减少判断写法', () => {
    function uploadFile(suffix, size) {
      // 允许上传的后缀名
      const suffixList = ['.zip', '.rar']
      const M = 1024*  1024
      if (!suffixList.includes(suffix)) return
      if (size > 10*  M) return
      return '下载成功'
    }
    uploadFile('.zip', 1*  1024*  1024)
  })
...

代码运行结果如下:

嵌套写法 x 888,445,014 ops/sec ±2.48% (88 runs sampled) 减少判断写法 x 905,763,884 ops/sec ±1.35% (92 runs sampled) 最快的是:减少判断写法,嵌套写法

尽管差距不大,但不适用嵌套的代码相比普通代码表现更优。

减少作用域链查找层级

减少代码中作用域链的查找层级也是优化手段之一,如下代码展示了两者的区别:

...
suite
  .add('before', () => {
    var name = '一碗粥'
    function sayMe() {
      name = '一碗周'
      function print() {
        var age = 18
        return name + age
      }
      print()
    }
    sayMe()
  })
  .add('after', () => {
    var name = '一碗粥'
    function sayMe() {
      var name = '一碗周' // 形成局部作用域
      function print() {
        var age = 18
        return name + age
      }
      print()
    }
    sayMe()
  })
...

代码运行结果如下:

before x 15,509,793 ops/sec ±7.78% (76 runs sampled) after x 17,930,066 ops/sec ±2.89% (83 runs sampled) 最快的是:after

上述代码仅用于展示区别,并无实际业务意义。

减少数据读取次数

如果对象中的某个数据在一个代码块中被使用两遍以上,将其缓存起来以减少数据读取次数,可以达到更优的性能。

测试代码如下:

...
var userList = {
  one: {
    name: '一碗周',
    age: 18,
  },
  two: {
    name: '一碗粥',
    age: 18,
  },
}
suite
  .add('before', () => {
    function returnOneInfo() {
      userList.one.info = userList.one.name + userList.one.age
    }
    returnOneInfo()
  })
  .add('after', () => {
    function returnOneInfo() {
      let one = userList.one
      one.info = one.name + one.age
    }
    returnOneInfo()
  })
...

代码运行结果如下:

before x 222,553,199 ops/sec ±16.63% (26 runs sampled) after x 177,894,903 ops/sec ±1.85% (88 runs sampled) 最快的是:before

字面量与构造式

凡是可以使用字面量方式声明的内容,绝对不应使用构造函数方式声明,两者在性能方面相差甚远,代码如下:

...
suite
  .add('before', () => {
    var str = new String('string')
  })
  .add('after', () => {
    var str = 'string'
  })
...

代码运行结果如下:

before x 38,601,223 ops/sec ±1.16% (89 runs sampled) after x 897,491,903 ops/sec ±0.92% (92 runs sampled) 最快的是:after

到此这篇关于总结分享10个JavaScript代码优化小tips的文章就介绍到这了,更多相关JavaScript优化tips内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章