Maison > interface Web > js tutoriel > Résumé des compétences knowledge_javascript en matière d'optimisation des performances JavaScript

Résumé des compétences knowledge_javascript en matière d'optimisation des performances JavaScript

WBOY
Libérer: 2016-05-16 15:30:52
original
933 Les gens l'ont consulté

Avec le développement d'Internet et l'amélioration de la vitesse du réseau et de la vitesse des machines, de plus en plus de sites Web utilisent une technologie client riche. Ajax est désormais la méthode la plus populaire. JavaScript est un langage interprété, donc s'il peut atteindre le même niveau que C/Java, cela limite ce qu'il peut faire sur le client. Afin d'améliorer ses performances, je souhaite m'appuyer sur ce que j'ai fait pour JavaScript auparavant. parler de ma propre expérience, dans l'espoir d'aider tout le monde à améliorer les performances de ses scripts JavaScript.

Avant-propos

J'ai appris JavaScript et j'ai également lu "Explication détaillée et pratique du noyau Jquery de développement Sharp". Je n'ai que deux mots pour évaluer ce livre : "Sharp". Il se peut que je ne comprenne pas parfaitement JavaScript. assez ou je suis trop stupide. Le plus souvent, je ne suis pas doué pour réfléchir et je suis trop paresseux pour penser, donc je n'ai pas une compréhension profonde d'une partie de l'essence de ce qui est dit.

Comme je veux m'améliorer et que je ne peux pas entrer dans un monde plus vaste, je dois trouver mon propre endroit pour bien vivre, j'accumule donc généralement des connaissances communes sur l'utilisation intentionnelle ou non de jQuery, en particulier pour les exigences de performances Ensemble, Je me demande toujours s'il existe une meilleure façon d'y parvenir.

Voici quelques conseils que j'ai résumés à titre de référence uniquement. (Je vais d'abord donner un titre général, puis utiliser un court paragraphe pour expliquer le sens, et enfin utiliser une démo pour expliquer brièvement)

Éviter la recherche globale

Dans une fonction, les objets globaux sont stockés en tant que variables locales pour réduire les recherches globales, car l'accès aux variables locales est plus rapide que l'accès aux variables globales

    function search() {
      //当我要使用当前页面地址和主机域名
      alert(window.location.href + window.location.host);
    }
    //最好的方式是如下这样 先用一个简单变量保存起来
    function search() {
      var location = window.location;
      alert(location.href + location.host);
    }
Copier après la connexion

Minuterie

Si vous ciblez du code qui s'exécute en permanence, vous ne devez pas utiliser setTimeout, mais setInterval, car setTimeout initialisera un minuteur à chaque fois, et setInterval n'initialisera un minuteur qu'au début

var timeoutTimes = 0;
    function timeout() {
      timeoutTimes++;
      if (timeoutTimes < 10) {
        setTimeout(timeout, 10);
      }
    }
    timeout();
    //可以替换为:
    var intervalTimes = 0;
    function interval() {
      intervalTimes++;
      if (intervalTimes >= 10) {
        clearInterval(interv);
      }
    }
    var interv = setInterval(interval, 10);
Copier après la connexion

Concaténation de chaînes

Si vous souhaitez concaténer plusieurs chaînes, vous devez utiliser less =, tel que

s+=a;
s+=b;
s+=c;
Copier après la connexion

doit être écrit sous la forme s =a b c

Si vous collectez des chaînes, par exemple en effectuant plusieurs opérations = sur la même chaîne, il est préférable d'utiliser un cache, d'utiliser des tableaux JavaScript pour collecter et enfin d'utiliser la méthode join pour les connecter

  var buf = [];
    for (var i = 0; i < 100; i++) {
      buf.push(i.toString());
    }
    var all = buf.join("");
Copier après la connexion

Éviter avec des déclarations

Semblable à une fonction, l'instruction with crée sa propre portée, augmentant ainsi la longueur de la chaîne de portée du code exécuté à l'intérieur. En raison de la recherche supplémentaire de la chaîne de portée, le code exécuté dans l'instruction with sera certainement plus long. que le code à l'extérieur. Le code qui s'exécute doit être lent et essayez de ne pas utiliser l'instruction with lorsque vous pouvez vous en passer.

 with (a.b.c.d) {
      property1 = 1;
      property2 = 2;
    }
    //可以替换为:
    var obj = a.b.c.d;
    obj.property1 = 1;
    obj.property2 = 2;
Copier après la connexion

Convertir les nombres en chaînes

Il est généralement préférable d'utiliser "" 1 pour convertir des nombres en chaînes. Même si cela semble un peu moche, c'est en fait le plus efficace, en termes de performances :

(“” +) > String() > .toString() > new String()
Copier après la connexion

Convertir un nombre à virgule flottante en entier

Beaucoup de gens aiment utiliser parseInt(). En fait, parseInt() est utilisé pour convertir des chaînes en nombres, pas entre des nombres à virgule flottante et des entiers. Nous devrions utiliser Math.floor() ou Math.round()<🎜. >

Diverses conversions de types

var myVar = "3.14159",
    str = "" + myVar, // to string 
    i_int = ~ ~myVar, // to integer 
    f_float = 1 * myVar, // to float 
    b_bool = !!myVar, /* to boolean - any string with length 
                and any number except 0 are true */
    array = [myVar]; // to array
Copier après la connexion

Si la méthode toString() est définie pour la conversion de type, il est recommandé d'appeler toString() explicitement, car l'opération interne essaiera la méthode toString() de l'objet pour voir s'il peut être converti en String après avoir essayé toutes les possibilités. Donc appeler cette méthode directement sera plus efficace

Déclarations de types multiples

En JavaScript, toutes les variables peuvent être déclarées à l'aide d'une seule instruction var, qui est une instruction combinée pour réduire le temps d'exécution de l'ensemble du script. Tout comme le code ci-dessus, le format de code ci-dessus est également assez standardisé, ce qui le rend. facile à lire. C'est clair.

Insérer un itérateur

Par exemple, var name=values[i]; i ; les deux premières instructions peuvent être écrites sous la forme var name=values[i ]

Utiliser des quantités directes

var aTest = new Array(); //替换为
    var aTest = [];
    var aTest = new Object; //替换为
    var aTest = {};
    var reg = new RegExp(); //替换为
    var reg = /../;
    //如果要创建具有一些特性的一般对象,也可以使用字面量,如下:
    var oFruit = new O;
    oFruit.color = "red";
    oFruit.name = "apple";
    //前面的代码可用对象字面量来改写成这样:
    var oFruit = { color: "red", name: "apple" };
Copier après la connexion

Utilisez DocumentFragment pour optimiser plusieurs ajouts

Une fois que vous devez mettre à jour le DOM, envisagez d'utiliser des fragments de document pour créer la structure du DOM, puis de l'ajouter au document existant.

for (var i = 0; i < 1000; i++) {
      var el = document.createElement('p');
      el.innerHTML = i;
      document.body.appendChild(el);
    }
    //可以替换为:
    var frag = document.createDocumentFragment();
    for (var i = 0; i < 1000; i++) {
      var el = document.createElement('p');
      el.innerHTML = i;
      frag.appendChild(el);
    }
    document.body.appendChild(frag);
Copier après la connexion

Utilisez l'affectation innerHTML une fois au lieu de construire l'élément dom

Pour les modifications importantes du DOM, l'utilisation de innerHTML est beaucoup plus rapide que l'utilisation des méthodes DOM standard pour créer la même structure DOM.

    var frag = document.createDocumentFragment();
    for (var i = 0; i < 1000; i++) {
      var el = document.createElement('p');
      el.innerHTML = i;
      frag.appendChild(el);
    }
    document.body.appendChild(frag);
    //可以替换为:
    var html = [];
    for (var i = 0; i < 1000; i++) {
      html.push('<p>' + i + '</p>');
    }
    document.body.innerHTML = html.join('');
Copier après la connexion

通过模板元素clone,替代createElement

很多人喜欢在JavaScript中使用document.write来给页面生成内容。事实上这样的效率较低,如果需要直接插入HTML,可以找一个容器元素,比如指定一个div或者span,并设置他们的innerHTML来将自己的HTML代码插入到页面中。通常我们可能会使用字符串直接写HTML来创建节点,其实这样做,1无法保证代码的有效性2字符串操作效率低,所以应该是用document.createElement()方法,而如果文档中存在现成的样板节点,应该是用cloneNode()方法,因为使用createElement()方法之后,你需要设置多次元素的属性,使用cloneNode()则可以减少属性的设置次数——同样如果需要创建很多元素,应该先准备一个样板节点

var frag = document.createDocumentFragment();
    for (var i = 0; i < 1000; i++) {
      var el = document.createElement('p');
      el.innerHTML = i;
      frag.appendChild(el);
    }
    document.body.appendChild(frag);
    //替换为:
    var frag = document.createDocumentFragment();
    var pEl = document.getElementsByTagName('p')[0];
    for (var i = 0; i < 1000; i++) {
      var el = pEl.cloneNode(false);
      el.innerHTML = i;
      frag.appendChild(el);
    }
    document.body.appendChild(frag);
Copier après la connexion

使用firstChild和nextSibling代替childNodes遍历dom元素

var nodes = element.childNodes;
    for (var i = 0, l = nodes.length; i < l; i++) {
      var node = nodes[i];
      //……
    }
    //可以替换为:
    var node = element.firstChild;
    while (node) {
      //……
      node = node.nextSibling;
Copier après la connexion

删除DOM节点

删除dom节点之前,一定要删除注册在该节点上的事件,不管是用observe方式还是用attachEvent方式注册的事件,否则将会产生无法回收的内存。另外,在removeChild和innerHTML=''二者之间,尽量选择后者. 因为在sIEve(内存泄露监测工具)中监测的结果是用removeChild无法有效地释放dom节点

使用事件代理

任何可以冒泡的事件都不仅仅可以在事件目标上进行处理,目标的任何祖先节点上也能处理,使用这个知识就可以将事件处理程序附加到更高的地方负责多个目标的事件处理,同样,对于内容动态增加并且子节点都需要相同的事件处理函数的情况,可以把事件注册提到父节点上,这样就不需要为每个子节点注册事件监听了。另外,现有的js库都采用observe方式来创建事件监听,其实现上隔离了dom对象和事件处理函数之间的循环引用,所以应该尽量采用这种方式来创建事件监听

重复使用的调用结果,事先保存到局部变量

   //避免多次取值的调用开销
    var h1 = element1.clientHeight + num1;
    var h2 = element1.clientHeight + num2;
    //可以替换为:
    var eleHeight = element1.clientHeight;
    var h1 = eleHeight + num1;
    var h2 = eleHeight + num2;
Copier après la connexion

注意NodeList

最小化访问NodeList的次数可以极大的改进脚本的性能

    var images = document.getElementsByTagName('img');
    for (var i = 0, len = images.length; i < len; i++) {
    }
Copier après la connexion

编写JavaScript的时候一定要知道何时返回NodeList对象,这样可以最小化对它们的访问

进行了对getElementsByTagName()的调用

获取了元素的childNodes属性
获取了元素的attributes属性
访问了特殊的集合,如document.forms、document.images等等
要了解了当使用NodeList对象时,合理使用会极大的提升代码执行速度

优化循环

可以使用下面几种方式来优化循环

减值迭代

大多数循环使用一个从0开始、增加到某个特定值的迭代器,在很多情况下,从最大值开始,在循环中不断减值的迭代器更加高效

简化终止条件

由于每次循环过程都会计算终止条件,所以必须保证它尽可能快,也就是说避免属性查找或者其它的操作,最好是将循环控制量保存到局部变量中,也就是说对数组或列表对象的遍历时,提前将length保存到局部变量中,避免在循环的每一步重复取值。

  var list = document.getElementsByTagName('p');
    for (var i = 0; i < list.length; i++) {
      //……
    }
    //替换为:
    var list = document.getElementsByTagName('p');
    for (var i = 0, l = list.length; i < l; i++) {
      //……
    }
Copier après la connexion

简化循环体

循环体是执行最多的,所以要确保其被最大限度的优化

使用后测试循环

在JavaScript中,我们可以使用for(;;),while(),for(in)三种循环,事实上,这三种循环中for(in)的效率极差,因为他需要查询散列键,只要可以,就应该尽量少用。for(;;)和while循环,while循环的效率要优于for(;;),可能是因为for(;;)结构的问题,需要经常跳转回去。

    var arr = [1, 2, 3, 4, 5, 6, 7];
    var sum = 0;
    for (var i = 0, l = arr.length; i < l; i++) {
      sum += arr[i];
    }
    //可以考虑替换为:
    var arr = [1, 2, 3, 4, 5, 6, 7];
    var sum = 0, l = arr.length;
    while (l--) {
      sum += arr[l];
    }
Copier après la connexion

最常用的for循环和while循环都是前测试循环,而如do-while这种后测试循环,可以避免最初终止条件的计算,因此运行更快。

展开循环

当循环次数是确定的,消除循环并使用多次函数调用往往会更快。

避免双重解释

如果要提高代码性能,尽可能避免出现需要按照JavaScript解释的字符串,也就是

尽量少使用eval函数

使用eval相当于在运行时再次调用解释引擎对内容进行运行,需要消耗大量时间,而且使用Eval带来的安全性问题也是不容忽视的。

不要使用Function构造器

不要给setTimeout或者setInterval传递字符串参数

var num = 0;
    setTimeout('num++', 10);
    //可以替换为:
    var num = 0;
    function addNum() {
      num++;
    }
    setTimeout(addNum, 10);
Copier après la connexion

缩短否定检测

    if (oTest != '#ff0000') {
      //do something
    }
    if (oTest != null) {
      //do something
    }
    if (oTest != false) {
      //do something
    }
    //虽然这些都正确,但用逻辑非操作符来操作也有同样的效果:
    if (!oTest) {
      //do something
    }
Copier après la connexion

条件分支

将条件分支,按可能性顺序从高到低排列:可以减少解释器对条件的探测次数
在同一条件子的多(>2)条件分支时,使用switch优于if:switch分支选择的效率高于if,在IE下尤为明显。4分支的测试,IE下switch的执行时间约为if的一半。

使用三目运算符替代条件分支

if (a > b) {
      num = a;
    } else {
      num = b;
    }
    //可以替换为:
    num = a > b &#63; a : b;
Copier après la connexion

使用常量

重复值:任何在多处用到的值都应该抽取为一个常量

用户界面字符串:任何用于显示给用户的字符串,都应该抽取出来以方便国际化
URLs:在Web应用中,资源位置很容易变更,所以推荐用一个公共地方存放所有的URL
任意可能会更改的值:每当你用到字面量值的时候,你都要问一下自己这个值在未来是不是会变化,如果答案是“是”,那么这个值就应该被提取出来作为一个常量。
避免与null进行比较

由于JavaScript是弱类型的,所以它不会做任何的自动类型检查,所以如果看到与null进行比较的代码,尝试使用以下技术替换

如果值应为一个引用类型,使用instanceof操作符检查其构造函数

如果值应为一个基本类型,作用typeof检查其类型

如果是希望对象包含某个特定的方法名,则使用typeof操作符确保指定名字的方法存在于对象上

避免全局量

全局变量应该全部字母大写,各单词之间用_下划线来连接。尽可能避免全局变量和函数, 尽量减少全局变量的使用,因为在一个页面中包含的所有JavaScript都在同一个域中运行。所以如果你的代码中声明了全局变量或者全局函数的话,后面的代码中载入的脚本文件中的同名变量和函数会覆盖掉(overwrite)你的。

//糟糕的全局变量和全局函数
var current = null;
function init(){
//...
}
function change() {
  //...
}
function verify() {
  //...
}
//解决办法有很多,Christian Heilmann建议的方法是:
//如果变量和函数不需要在“外面”引用,那么就可以使用一个没有名字的方法将他们全都包起来。
(function(){
var current = null;
function init() {
  //...
}
function change() {
  //...
}
function verify() {
  //...
}
})();
//如果变量和函数需要在“外面”引用,需要把你的变量和函数放在一个“命名空间”中
//我们这里用一个function做命名空间而不是一个var,因为在前者中声明function更简单,而且能保护隐私数据
myNameSpace = function() {
  var current = null;
  function init() {
    //...
  }
  function change() {
    //...
  }
  function verify() {
    //...
  }
//所有需要在命名空间外调用的函数和属性都要写在return里面
  return {
    init: init,
    //甚至你可以为函数和属性命名一个别名
    set: change
  };
};
Copier après la connexion

尊重对象的所有权

因为JavaScript可以在任何时候修改任意对象,这样就可以以不可预计的方式覆写默认的行为,所以如果你不负责维护某个对象,它的对象或者它的方法,那么你就不要对它进行修改,具体一点就是说:

不要为实例或原型添加属性
不要为实例或者原型添加方法
不要重定义已经存在的方法
不要重复定义其它团队成员已经实现的方法,永远不要修改不是由你所有的对象,你可以通过以下方式为对象创建新的功能:
创建包含所需功能的新对象,并用它与相关对象进行交互
创建自定义类型,继承需要进行修改的类型,然后可以为自定义类型添加额外功能

循环引用

如果循环引用中包含DOM对象或者ActiveX对象,那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前,即使是刷新页面,这部分内存不会被浏览器释放。

简单的循环引用:

    var el = document.getElementById('MyElement');
    var func = function () {
      //…
    }
    el.func = func;
    func.element = el;
Copier après la connexion

但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。

    function init() {
      var el = document.getElementById('MyElement');
      el.onclick = function () {
        //……
      }
    }
    init();
Copier après la connexion

init在执行的时候,当前上下文我们叫做context。这个时候,context引用了el,el引用了function,function引用了context。这时候形成了一个循环引用。

下面2种方法可以解决循环引用:

1) 置空dom对象

function init() {
      var el = document.getElementById('MyElement');
      el.onclick = function () {
        //……
      }
    }
    init();
    //可以替换为:
    function init() {
      var el = document.getElementById('MyElement');
      el.onclick = function () {
        //……
      }
      el = null;
    }
    init(); 
Copier après la connexion

将el置空,context中不包含对dom对象的引用,从而打断循环应用。

如果我们需要将dom对象返回,可以用如下方法:

   function init() {
      var el = document.getElementById('MyElement');
      el.onclick = function () {
        //……
      }
      return el;
    }
    init();
    //可以替换为:
    function init() {
      var el = document.getElementById('MyElement');
      el.onclick = function () {
        //……
      }
      try {
        return el;
      } finally {
        el = null;
      }
    }
    init();
Copier après la connexion

2) 构造新的context

  function init() {
      var el = document.getElementById('MyElement');
      el.onclick = function () {
        //……
      }
    }
    init();
    //可以替换为:
    function elClickHandler() {
      //……
    }
    function init() {
      var el = document.getElementById('MyElement');
      el.onclick = elClickHandler;
    }
    init();
Copier après la connexion

把function抽到新的context中,这样,function的context就不包含对el的引用,从而打断循环引用。

通过javascript创建的dom对象,必须append到页面中

IE下,脚本创建的dom对象,如果没有append到页面中,刷新页面,这部分内存是不会回收的!

    function create() {
      var gc = document.getElementById('GC');
      for (var i = 0; i < 5000; i++) {
        var el = document.createElement('div');
        el.innerHTML = "test";
        //下面这句可以注释掉,看看浏览器在任务管理器中,点击按钮然后刷新后的内存变化
        gc.appendChild(el);
      }
    }
Copier après la connexion

释放dom元素占用的内存

将dom元素的innerHTML设置为空字符串,可以释放其子元素占用的内存。

在rich应用中,用户也许会在一个页面上停留很长时间,可以使用该方法释放积累得越来越多的dom元素使用的内存。

释放javascript对象

在rich应用中,随着实例化对象数量的增加,内存消耗会越来越大。所以应当及时释放对对象的引用,让GC能够回收这些内存控件。

对象:obj = null

对象属性:delete obj.myproperty

数组item:使用数组的splice方法释放数组中不用的item

避免string的隐式装箱

对string的方法调用,比如'xxx'.length,浏览器会进行一个隐式的装箱操作,将字符串先转换成一个String对象。推荐对声明有可能使用String实例方法的字符串时,采用如下写法:

var myString = new String(‘Hello World');
Copier après la connexion

松散耦合

1、解耦HTML/JavaScript

JavaScript和HTML的紧密耦合:直接写在HTML中的JavaScript、使用包含内联代码的

Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal