写在前面

想要做到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)
最快的是:缓存的另一种写法

选择最优的循环方式

我们现在常用的循环有forEachforfor...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内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

总结分享10个JavaScript代码优化小tips的更多相关文章

  1. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  2. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. amaze ui 的使用详细教程

    这篇文章主要介绍了amaze ui 的使用详细教程,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. html5简介_动力节点Java学院整理

    这篇文章主要介绍了html5简介,用于指定构建网页的元素,这些元素中的大多数都用于描述网页内容,有兴趣的可以了解一下

  5. ios 8 Homescreen webapp,关闭和打开iPad停止javascript

    我有一个适用于iPad的全屏HTML5网络应用程序,并且刚刚安装了IOS8来试用它,它一切正常,直到你关闭并重新启动iPad.一旦web应用程序重新启动javascript就会停止并加载新页面不会重新启动它.在iPad上的Safari中打开同一页面时,关闭和打开iPad会继续按预期工作.其他人注意到了这个或想出了一个解决方案吗?解决方法这似乎是我在iOS8.1.1更新中解决的.

  6. iOS 6 javascript与object.defineProperty的间歇性问题

    当访问使用较新的Object.defineProperty语法定义属性的对象的属性时,有没有其他人注意到新iOS6javascript引擎中的间歇性错误/问题?https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty我正在看到javascript失败的情况,说

  7. ios – 如何使用JSExport导出内部类的方法

    解决方法似乎没有办法将内部类函数导出到javascript.我将内部类移出并创建了独立的类,它起作用了.

  8. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  9. ios – 内存泄漏与UIWebView和Javascript

    清楚地包含一个Javascript文件到我的HTML是使UIWebView泄漏内存.当我重复使用相同的UIWebView对象时,或者每当我有内容实例化一个新的漏洞时,会出现泄漏的事实,导致我认为必须有一些JavaScript文件被loadHTMLString处理,导致泄漏.有人知道如何解决这个问题吗?

  10. iOS应用程序的UI自动化测试如何与乐器和Javascript

    从WWDC2010视频会议中了解iOS应用程序的自动化UI测试,但没有实践.从代码项目project,我们可以有一个例子.这个问题在这里听到有涉及这个的人.任何限制?解决方法我建议从AlexWollmer开始使用thisblogpost.他创建了一个非常有用的JavaScript库:tuneup_jswithtest()函数,它允许测试分离和有用的帮助者以及为自动化仪器编写测试的断言.

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部