Maison > interface Web > js tutoriel > le corps du texte

Résumé et partage de 10 conseils d'optimisation du code JavaScript

WBOY
Libérer: 2022-08-01 15:00:38
avant
1585 Les gens l'ont consulté

Cet article vous apporte des connaissances pertinentes sur javascript. Il présente et résume principalement 10 conseils d'optimisation du code JavaScript. L'article présente le contenu en détail autour du sujet. J'espère qu'il sera utile à tout le monde.

Résumé et partage de 10 conseils d'optimisation du code JavaScript

[Recommandations associées : Tutoriel vidéo Javascript, front-end Web]

Écrit devant

Si vous souhaitez optimiser le code JavaScript, la première chose que vous devez faire est de tester avec précision le code JavaScript. temps d'exécution du code. En fait, ce qu'il faut faire est de collecter un grand nombre d'échantillons d'exécution pour les statistiques et l'analyse mathématiques. Ici, nous utilisons benchmark.js pour détecter l'exécution du code. benchmark.js来检测代码的执行情况。

首先我们需要在项目中安装依赖,代码如下:

yarn add benchmark --save
# 或者
npm i benchmark --save
Copier après la connexion

然后我们写一个测试代码,如下所示:

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 })

复制代码
Copier après la connexion

代码执行结果如下:

// 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表示的是每秒执行的次数,当然是越大越好,紧接着是每秒执行次数上下相差的百分比,最后括号中的内容表示共取样多少次。我们可以看到,都是join1000的性能更好一些(我感觉我在说废话)。

慎用全局变量

这里所说的慎用全局变量,为什么要慎用呢?主要有以下几点:

  • 全局变量定义在全局执行上下文,是所有作用域链的顶端。每次查找的时候都从局部找到最顶端,在时间上会有所消耗。
  • 全局执行上下文一直存在于上下文的执行栈,直到程序退出,才会被销毁,内存空间浪费 。
  • 如果某个局部作用域出现了同名的变量则会遮盖或者说污染全局变量 。

下面我们就来写一段代码,看一下全局变量与布局变量在执行效率方面的差异,代码如下:

...
suite
  .add('全局变量', () => {
    // 该函数内模拟全局作用域
    let i,
      str = ''
    for (i = 0; i < 1000; i++) {
      str += i
    }
  })
  .add(&#39;局部变量&#39;, () => {
    for (let i = 0, str = &#39;&#39;; i < 1000; i++) {
      str += i
    }
  })
...
Copier après la connexion

代码运行结果如下:

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

虽然说差异不大,但是我们可以感知全局变量比局部的性能更差一些。

通过原型新增方法

为构造函数增加实例对象需要的方法时,尽量使用原型的方式添加,而不是构造函数内部进行添加,我们可以看如下测试代码:

...
suite
  .add(&#39;构造函数内部添加&#39;, () => {
    function Person() {
      this.sayMe = function () {
        return &#39;一碗周&#39;
      }
    }
    let p = new Person()
  })
  .add(&#39;原型方式内部添加&#39;, () => {
    function Person() {}
    Person.prototype.sayMe = function () {
      return &#39;一碗周&#39;
    }
    let p = new Person()
  })
...
Copier après la connexion

代码运行结果如下:

构造函数内部添加 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(&#39;使用属性访问方法&#39;, () => {
    function Person() {
      this.name = &#39;一碗周&#39;
      this.getName = function () {
        return &#39;一碗周&#39;
      }
    }
    let p = new Person()
    let n = p.getName()
  })
  .add(&#39;不使用属性访问方法&#39;, () => {
    function Person() {
      this.name = &#39;一碗周&#39;
    }
    let p = new Person()
    let n = p.name
  })
...
Copier après la connexion

代码运行结果如下:

使用属性访问方法 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(&#39;正序&#39;, () => {
    let arr = new Array(100)
    let str = &#39;&#39;
    for (let i = 0; i < arr.length; i++) {
      str += i
    }
  })
  .add(&#39;缓存&#39;, () => {
    let arr = new Array(100)
    let str = &#39;&#39;
    for (let i = arr.length; i; i--) {
      str += i
    }
  })
  .add(&#39;缓存的另一种写法&#39;, () => {
    let arr = new Array(100)
    let str = &#39;&#39;
    for (let i = 0, l = arr.length; i < l; i++) {
      str += i
    }
  })
...
Copier après la connexion

代码运行结果如下:

正序 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

Nous devons d'abord installer les dépendances dans le projet, le code est le suivant :

...
suite
  .add(&#39;forEach&#39;, () => {
    let arr = new Array(100)
    let str = &#39;&#39;
    arr.forEach(i => {
      str += i
    })
  })
  .add(&#39;for...in&#39;, () => {
    let arr = new Array(100)
    let str = &#39;&#39;
    for (i in arr) {
      str += i
    }
  })
  .add(&#39;for&#39;, () => {
    let arr = new Array(100)
    let str = &#39;&#39;
    for (let i = 0, l = arr.length; i < l; i++) {
      str += i
    }
  })
...
Copier après la connexion

🎜Ensuite, nous écrivons un code de test comme suit : 🎜🎜
...
/*** 
 接收两类文件,zip 和 rar* 
 压缩包的大小限制为 10 兆* 
/

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

      if (suffixList.includes(suffix)) {
        if (size <= 10*  M) {
          return &#39;下载成功&#39;
        }
      }
    }
    uploadFile(&#39;.zip&#39;, 1*  1024*  1024)
  })
  .add(&#39;减少判断写法&#39;, () => {
    function uploadFile(suffix, size) {
      // 允许上传的后缀名
      const suffixList = [&#39;.zip&#39;, &#39;.rar&#39;]
      const M = 1024*  1024
      if (!suffixList.includes(suffix)) return
      if (size > 10*  M) return
      return &#39;下载成功&#39;
    }
    uploadFile(&#39;.zip&#39;, 1*  1024*  1024)
  })
...
Copier après la connexion
Copier après la connexion
🎜🎜Le résultat de l'exécution du code est le suivant : 🎜🎜
🎜// join1000 x 146 854 ops/sec ±1,86 % (88 exécutions échantillonnées)
// join10000 x 16 083 ops/sec ±1,06 % (92 exécutions échantillonnées)
// Le plus rapide est : join1000🎜
🎜Dans les résultats, ops/sec représente le nombre d'exécutions par seconde. Bien sûr, plus c'est grand, mieux c'est, suivi de la différence en pourcentage entre le nombre supérieur et inférieur d'exécutions par seconde. . Enfin, le contenu entre parenthèses représente l'échantillonnage total combien de fois. On voit que les performances de join1000 sont meilleures (j'ai l'impression de dire des bêtises). 🎜🎜Utilisez les variables globales avec prudence🎜🎜🎜Les variables globales mentionnées ici doivent être utilisées avec prudence Pourquoi doivent-elles être utilisées avec prudence ? Les principaux points sont les suivants : 🎜🎜
  • Les variables globales sont définies dans le contexte d'exécution global et constituent le sommet de toutes les chaînes de portée. Chaque fois que vous effectuez une recherche, vous devez trouver la partie supérieure localement, ce qui prendra du temps. 🎜
  • Le contexte d'exécution global existe toujours dans la pile d'exécution du contexte et ne sera détruit qu'à la sortie du programme, ce qui est une perte d'espace mémoire.
  • Si une variable du même nom apparaît dans une portée locale, elle couvrira ou polluera la variable globale.
🎜🎜Écrivons un morceau de code pour voir la différence d'efficacité d'exécution entre les variables globales et les variables de mise en page. Le code est le suivant :🎜🎜
...
suite
  .add(&#39;before&#39;, () => {
    var name = &#39;一碗粥&#39;
    function sayMe() {
      name = &#39;一碗周&#39;
      function print() {
        var age = 18
        return name + age
      }
      print()
    }
    sayMe()
  })
  .add(&#39;after&#39;, () => {
    var name = &#39;一碗粥&#39;
    function sayMe() {
      var name = &#39;一碗周&#39; // 形成局部作用域
      function print() {
        var age = 18
        return name + age
      }
      print()
    }
    sayMe()
  })
...
Copier après la connexion
Copier après la connexion
🎜🎜Les résultats de l'exécution du code sont les suivants :🎜. 🎜
🎜Variable globale x 158 697 ops/sec ±1,05 % (87 exécutions échantillonnées)
Variable locale x 160 697 ops/sec ±1,03 % (90 exécutions échantillonnées)
Le plus rapide est : variable locale 🎜
🎜Bien que la différence ne soit pas grande, on sent que les performances des variables globales sont moins bonnes que celles des variables locales. 🎜🎜Ajouter des méthodes via le prototype🎜🎜Lors de l'ajout de méthodes requises pour les objets d'instance au constructeur, essayez de les ajouter à l'aide du prototype au lieu de les ajouter à l'intérieur du constructeur. Nous pouvons regarder le code de test suivant : 🎜
...
var userList = {
  one: {
    name: &#39;一碗周&#39;,
    age: 18,
  },
  two: {
    name: &#39;一碗粥&#39;,
    age: 18,
  },
}
suite
  .add(&#39;before&#39;, () => {
    function returnOneInfo() {
      userList.one.info = userList.one.name + userList.one.age
    }
    returnOneInfo()
  })
  .add(&#39;after&#39;, () => {
    function returnOneInfo() {
      let one = userList.one
      one.info = one.name + one.age
    }
    returnOneInfo()
  })
...
Copier après la connexion
Copier après la connexion
🎜🎜Le code en cours d'exécution. les résultats sont les suivants :🎜🎜
🎜Le constructeur ajoute en interne x 573 786 ops/sec ±1,97% (89 exécutions échantillonnées)
La méthode prototype ajoute en interne x 581 693 ops/sec ±3,46% (80 exécutions échantillonnées)
Le plus rapide : ajoutez 🎜
🎜 à l'intérieur du constructeur pour éviter les fuites de mémoire dans les fermetures 🎜🎜 Puisque les fermetures entraîneront la sauvegarde des variables de la fonction en mémoire, la consommation de mémoire est très importante, donc les fermetures ne peut pas être abusé, sinon cela entraînerait des problèmes de performances sur la page Web et pourrait même entraîner des fuites de mémoire. La solution est de supprimer toutes les variables locales inutilisées avant de quitter la fonction (c'est-à-dire de réaffecter les variables locales à null). 🎜🎜Évitez d'utiliser des méthodes d'accès aux propriétés🎜🎜Dans les objets en JavaScript, évitez d'utiliser certaines méthodes d'accès aux propriétés en effet, car toutes les propriétés en JavaScript sont visibles de l'extérieur. 🎜🎜🎜L'exemple de code est le suivant : 🎜🎜
...
suite
  .add(&#39;before&#39;, () => {
    var str = new String(&#39;string&#39;)
  })
  .add(&#39;after&#39;, () => {
    var str = &#39;string&#39;
  })
...
Copier après la connexion
Copier après la connexion
🎜🎜Le résultat de l'exécution du code est le suivant : 🎜🎜
🎜Utilisation de la méthode d'accès aux attributs x 406 682 ops/sec ±2,33 % (82 exécutions échantillonnées)
Ne pas utiliser la méthode d'accès aux attributs x 554 169 ops/sec ±2,03 % (85 exécutions échantillonnées)
Le plus rapide est : ne pas utiliser la méthode d'accès aux attributs🎜
🎜for optimisation de la boucle🎜🎜Lorsque nous utilisons le for boucle, nous pouvons mettre certaines données nécessaires. Les propriétés de mise en cache telles que arr.length n'ont pas besoin d'être obtenues à chaque fois qu'elles sont jugées, optimisant ainsi notre code. 🎜🎜🎜L'exemple de code est le suivant : 🎜🎜rrreee🎜🎜Les résultats de l'exécution du code sont les suivants : 🎜🎜
🎜Séquence avant x 1 322 889 ops/sec ±1,36 % (86 exécutions échantillonnées)
Cache x 1 356 696 ops/sec ±0,70 % (92 exécutions échantillonnées)
Une autre façon d'écrire le cachex 1 383 091 ops/sec ±0,70 % (93 exécutions échantillonnées)
La plus rapide est : une autre façon d'écrire le cache🎜
🎜 Choisissez la méthode de boucle optimale 🎜🎜Les boucles que nous utilisons couramment incluent désormais les boucles forEach, for et for...in. ces types sont-ils ? Il a les meilleures performances. Le code de test est le suivant : 🎜rrreee🎜🎜Les résultats de l'exécution du code sont les suivants : 🎜🎜

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(&#39;嵌套写法&#39;, () => {
    function uploadFile(suffix, size) {
      // 允许上传的后缀名
      const suffixList = [&#39;.zip&#39;, &#39;.rar&#39;]
      const M = 1024*  1024

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

代码运行结果如下:

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

虽然说差距并不是很大,但是不适用嵌套的代码比普通代码更优一些。

减少作用域链查找层级

减少代码中作用域链的查找也是代码优化的一种方法,如下代码展示了两者的区别:

...
suite
  .add(&#39;before&#39;, () => {
    var name = &#39;一碗粥&#39;
    function sayMe() {
      name = &#39;一碗周&#39;
      function print() {
        var age = 18
        return name + age
      }
      print()
    }
    sayMe()
  })
  .add(&#39;after&#39;, () => {
    var name = &#39;一碗粥&#39;
    function sayMe() {
      var name = &#39;一碗周&#39; // 形成局部作用域
      function print() {
        var age = 18
        return name + age
      }
      print()
    }
    sayMe()
  })
...
Copier après la connexion
Copier après la connexion

代码运行结果如下:

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: &#39;一碗周&#39;,
    age: 18,
  },
  two: {
    name: &#39;一碗粥&#39;,
    age: 18,
  },
}
suite
  .add(&#39;before&#39;, () => {
    function returnOneInfo() {
      userList.one.info = userList.one.name + userList.one.age
    }
    returnOneInfo()
  })
  .add(&#39;after&#39;, () => {
    function returnOneInfo() {
      let one = userList.one
      one.info = one.name + one.age
    }
    returnOneInfo()
  })
...
Copier après la connexion
Copier après la connexion

代码运行结果如下:

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(&#39;before&#39;, () => {
    var str = new String(&#39;string&#39;)
  })
  .add(&#39;after&#39;, () => {
    var str = &#39;string&#39;
  })
...
Copier après la connexion
Copier après la connexion

代码运行结果如下:

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

【相关推荐:javascript视频教程web前端

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:jb51.net
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal