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

Résumé des connaissances sur l'optimisation des performances JavaScript

黄舟
Libérer: 2017-02-25 13:23:53
original
1611 Les gens l'ont consulté

Les problèmes de performances de JavaScript ne peuvent pas être sous-estimés, ce qui oblige les développeurs à prêter plus d'attention à certains détails lors de l'écriture de programmes JavaScript. Cet article présente de manière très détaillée les points de connaissance de l'optimisation des performances de JavaScript, ce qui est certainement une information utile.

Avant-propos

J'ai appris le javascript et j'ai également lu "Développement pointu de l'explication détaillée et de la pratique du noyau Jquery". Il n'y a que deux mots pour ce livre : pointu, peut-être à cause de javascript, je ne le comprends pas assez bien, ou je suis trop stupide, plus important encore, je ne suis pas doué pour réfléchir et je suis trop paresseux pour réfléchir, donc je n'ai pas une compréhension approfondie d'une partie de l'essence. de celui-ci.

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 de jQuery intentionnellement ou non, en particulier pour Quand il En ce qui concerne les exigences de performance, 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

Global sera utilisé dans une fonction Les objets 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

Timer

If vous ciblez du code exécuté en continu, ne doit pas utiliser setTimeout, mais doit utiliser setInterval, car setTimeout initialisera une minuterie à chaque fois et setInterval ne sera initialisé qu'au début d'une minuterie

.

        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;

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

s'il s'agit d'une collection de caractères String, par exemple ; , si vous effectuez plusieurs fois l'opération = sur la même chaîne, il est préférable d'utiliser un cache, d'utiliser un tableau JavaScript pour collecter, et enfin 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 les instructions with

Semblable aux fonctions, l'instruction with crée sa propre portée et augmente donc la longueur de la chaîne de portée du code exécuté en son sein, en raison aux effets supplémentaires Pour la recherche de chaîne de domaines, le code exécuté dans l'instruction with sera certainement plus lent que le code exécuté à l'extérieur. Essayez de ne pas utiliser l'instruction with lorsque ne peut pas être utilisé.

 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 des 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 ressemble à A un peu plus moche, mais en fait c'est le plus efficace, en termes de performances :

("" ) > String() >

Convertir les nombres à virgule flottante en entiers

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. conversion, nous devrions utiliser Math.floor() ou Math.round()

Divers types de conversion

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 défini Si le La méthode toString() est utilisée pour la conversion de type, il est recommandé d'appeler explicitement toString()

, 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, de sorte que les instructions soient combinées entre elles pour réduire l'ensemble. Le temps d'exécution du script est le même que celui du code ci-dessus. Le format du code ci-dessus est également assez standardisé, ce qui est facile à comprendre en un coup d'œil.

Insérer un itérateur

tel que 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 ajoutez-le Ajouter au document existant.

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

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

Pour les modifications importantes du DOM, utilisez innerHTML plutôt que d'utiliser le DOM standard méthode Créer la même structure DOM est beaucoup plus rapide.

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

Clonez l'élément de modèle au lieu de createElement

De nombreuses personnes aiment utiliser document.write en JavaScript pour générer du contenu pour la page . En fait, c'est moins efficace si vous devez insérer du HTML directement, vous pouvez trouver un élément conteneur, tel que la spécification d'un p ou d'un span, et définir son innerHTML pour insérer votre propre code HTML dans la page. Habituellement, nous pouvons utiliser des chaînes pour écrire du HTML directement afin de créer des nœuds. En fait, si nous faisons cela, 1. la validité du code ne peut pas être garantie et 2. l'efficacité de l'opération sur les chaînes est faible, donc la méthode document.createElement() devrait le faire. être utilisé, et s'il y a un élément prêt à l'emploi dans le document. Pour les nœuds de modèle, vous devez utiliser la méthode cloneNode(), car après avoir utilisé la méthode createElement(), vous devez définir les attributs de l'élément plusieurs fois en utilisant. cloneNode() peut réduire le nombre de paramètres d'attributs - de même, si vous devez créer de nombreux éléments, vous devez d'abord préparer un exemple de nœud

        var frag = document.createDocumentFragment();
        for (var i = 0; i < 1000; i++) {
            var el = document.createElement(&#39;p&#39;);
            el.innerHTML = i;
            frag.appendChild(el);
        }
        document.body.appendChild(frag);
        //替换为:
        var frag = document.createDocumentFragment();
        var pEl = document.getElementsByTagName(&#39;p&#39;)[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

Utiliser firstChild et nextSibling au lieu de childNodes pour parcourir les éléments 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(&#39;img&#39;);
        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(&#39;p&#39;);
        for (var i = 0; i < list.length; i++) {
            //……
        }

        //替换为:
        var list = document.getElementsByTagName(&#39;p&#39;);
        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(&#39;num++&#39;, 10);
        //可以替换为:
        var num = 0;
        function addNum() {
            num++;
        }
        setTimeout(addNum, 10);
Copier après la connexion

缩短否定检测

       if (oTest != &#39;#ff0000&#39;) {
            //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 ? 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(&#39;MyElement&#39;);
        var func = function () {
            //…
        }
        el.func = func;
        func.element = el;
Copier après la connexion

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

        function init() {
            var el = document.getElementById(&#39;MyElement&#39;);
            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(&#39;MyElement&#39;);
            el.onclick = function () {
                //……
            }
        }
        init();
        //可以替换为:
        function init() {
            var el = document.getElementById(&#39;MyElement&#39;);
            el.onclick = function () {
                //……
            }
            el = null;
        }
        init();
Copier après la connexion

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

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

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

2) 构造新的context

        function init() {
            var el = document.getElementById(&#39;MyElement&#39;);
            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(&#39;GC&#39;);
            for (var i = 0; i < 5000; i++) {
                var el = document.createElement(&#39;p&#39;);
                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’);

松散耦合

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