Heim >Web-Frontend >js-Tutorial >Praktische Leistung von JavaScript
1. Auf den Umfang achten
Globale Suche vermeiden
Ein Beispiel:
function updateUI(){ var imgs = document.getElementByTagName("img"); for(var i=0, len=imgs.length; i<len; i++){ imgs[i].title = document.title + " image " + i; } var msg = document.getElementById("msg"); msg.innnerHTML = "Update complete."; }
Diese Funktion sieht vielleicht ganz normal aus, enthält aber drei A-Referenzen das globale Dokumentobjekt. Wenn auf der Seite mehrere Bilder vorhanden sind, wird der Dokumentverweis in der for-Schleife mehrmals oder sogar hunderte Male ausgeführt und jedes Mal wird eine Bereichskettensuche durchgeführt. Indem Sie eine lokale Variable erstellen, die auf das Dokumentobjekt verweist, können Sie die Leistung dieser Funktion verbessern, indem Sie eine globale Suche einschränken:
function updateUI(){ var doc = document; var imgs = doc.getElementByTagName("img"); for(var i=0, len=imgs.length; i<len; i++){ imgs[i].title = doc.title + " image " + i; } var msg = doc.getElementById("msg"); msg.innnerHTML = "Update complete."; }
Speichern Sie hier zuerst das Dokumentobjekt in der lokalen Dokumentvariablen und dann in der verbleibend Ersetzen Sie das Originaldokument im Code. Im Vergleich zur Originalversion verfügt die Funktion jetzt nur noch über einen globalen Lookup, was definitiv schneller ist.
2. Wählen Sie die richtige Methode
1. Vermeiden Sie unnötige Attributsuchen
Konstante Werte zu erhalten ist ein sehr effizienter Prozess
var value = 5; var sum = 10 + value; alert(sum);
Dies Code Es wurden vier Konstantwertsuchen durchgeführt: die Zahl 5, der Variablenwert, die Zahl 10 und die Variablensumme.
Der Zugriff auf Array-Elemente in JavaScript ist genauso effizient wie die einfache Variablensuche. Daher ist der folgende Code genauso effizient wie das vorherige Beispiel:
var value = [5,10]; var sum = value[0] + value[1]; alert(sum);
Jede Eigenschaftssuche für das Objekt dauert länger als der Zugriff auf eine Variable oder ein Array, da die Eigenschaft mit diesem Namen einmal im Prototyp durchgeführt werden muss Kette Suchelement. Je mehr Attributsuchen vorhanden sind, desto länger ist die Ausführungszeit.
var values = {first: 5, second: 10}; var sum = values.first + values.second; alert(sum);
Dieser Code verwendet zwei Attributsuchen, um den Wert der Summe zu berechnen. Das ein- oder zweimalige Durchführen einer Attributsuche führt nicht zu nennenswerten Leistungsproblemen, aber hunderte oder tausende davon werden die Ausführung definitiv verlangsamen.
Beachten Sie mehrere Attributsuchen, die einen einzelnen Wert erhalten. Zum Beispiel:
var query = window.location.href.substring(window.location.href.indexOf("?"));
In diesem Code gibt es 6 Eigenschaftssuchen: 3 Mal für window.location.href.substring() und 3 Mal für window.location.href.indexOf(). Zählen Sie einfach die Anzahl der Punkte im Code, um die Anzahl der Suchvorgänge zu bestimmen. Dieser Code verwendet window.location.href zweimal und führt dieselbe Suche zweimal durch, sodass die Effizienz besonders schlecht ist.
Sobald eine Objekteigenschaft mehrfach verwendet wird, sollte sie in einer lokalen Variablen gespeichert werden. Der vorherige Code kann wie folgt umgeschrieben werden:
var url = window.locaiton.href; var query = url.substring(url.indexOf("?"));
Diese Version des Codes verfügt nur über 4 Attributsuchen, was 33 % im Vergleich zur Originalversion spart.
Im Allgemeinen sollte die Komplexität des Algorithmus so weit wie möglich reduziert werden, solange dies möglich ist. Verwenden Sie lokale Variablen, um Eigenschaftssuchen so weit wie möglich durch Wertesuchen zu ersetzen, und verwenden Sie außerdem numerische Speicherorte, wenn auf sie entweder über numerische Array-Speicherorte oder über benannte Eigenschaften (z. B. NodeList-Objekte) zugegriffen werden kann.
2. Schleife optimieren
Die grundlegenden Optimierungsschritte einer Schleife sind wie folgt.
(1) Iteration dekrementieren – Die meisten Schleifen verwenden einen Iterator, der bei 0 beginnt und auf einen bestimmten Wert ansteigt. In vielen Fällen ist es effizienter, einen Iterator ausgehend vom Maximalwert durch die Schleife zu dekrementieren.
(2) Vereinfachen Sie die Beendigungsbedingung – Da die Beendigungsbedingung jedes Mal berechnet wird, wenn der Schleifenvorgang ausgeführt wird, muss sichergestellt werden, dass sie so schnell wie möglich ist. Dies bedeutet, dass die Suche nach Eigenschaften oder andere Vorgänge vermieden werden müssen.
(3) Vereinfachen Sie den Schleifenkörper – die Schleife wird am häufigsten ausgeführt. Stellen Sie daher sicher, dass sie maximal optimiert ist und stellen Sie sicher, dass einige andere intensive Berechnungen der Schleife problemlos entfernt werden können.
(4 Verwenden Sie Post-Test-Schleifen – die am häufigsten verwendeten Schleifen und While-Schleifen sind Pre-Test-Schleifen. Post-Test-Schleifen wie do-while können die Berechnung der anfänglichen Beendigungsbedingung vermeiden läuft schneller.
Das Folgende ist eine grundlegende for-Schleife:
for(var i=0; i < value.length; i++){ process(values[i]); }
In diesem Code erhöht sich die Variable i von 0 auf die Gesamtzahl der Elemente im Wertearray Die Schleife kann wie folgt in eine Dekrementierung von i geändert werden:
for(var i=value.length -1; i >= 0; i--){ process(values[i]); }
Die Beendigungsbedingung wird von value.length auf 0 vereinfacht. Die
-Schleife kann auch in einen Beitrag geändert werden -Testschleife wie folgt:
var i=values.length -1; if (i> -1){ do{ process(values[i]) }while(--i>=0) //此处有个勘误,书上终止条件为(--i>0),经测试,(--i>=0)才是正确的 }
Die wichtigste Optimierung besteht darin, die Beendigungsbedingung und den Dekrementoperator in einer einzigen Anweisung zu kombinieren.
Denken Sie daran, dass Sie bei Verwendung einer „Post-Test“-Schleife sicherstellen müssen, dass die zu verarbeitenden Werte mindestens 1 sind. Ein leeres Array führt zu einer zusätzlichen Schleife und die „Pre-Test“-Schleife kann vermieden werden
3. Entrollen Sie die Schleife
Wenn die Anzahl der Schleifen bestimmt ist, entfernen Sie die Schleife und verwenden Sie sie mehrmals. Angenommen, das Wertearray enthält nur 3 Elemente , indem Sie „process()“ direkt für jedes Element aufrufen. Dadurch entfällt der zusätzliche Aufwand für die Einrichtung der Schleife und die Verarbeitung der Beendigungsbedingung, wodurch der Code schneller ausgeführt wird. 🎜>
Wenn die Anzahl der Iterationen in der Schleife nicht im Voraus bestimmt werden kann , können Sie die Verwendung einer Technik namens Duff-Gerät in Betracht ziehen. Das Grundkonzept des Duff-Geräts besteht darin, eine Schleife in eine Reihe zu erweitern, indem berechnet wird, ob die Anzahl der Iterationen ein Vielfaches von 8 ist.//消除循环 process(values[0]); process(values[1]); process(values[2]);
Andrew B. King schlug eine schnellere Duff-Gerätetechnik vor, indem er die Do-While-Schleife in zwei separate Schleifen aufteilte. Hier ist ein Beispiel:
var iterations = Math.floor(values.length / 8); var leftover = values.length % 8; var i = 0; if(leftover>0){ do{ process(values[i++]); }while(--leftover > 0); } do{ process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); }while(--iterations > 0);
在这个实现中,剩余的计算部分不会在实际循环中处理,而是在一个初始化循环中进行除以8的操作。当处理掉了额外的元素,继续执行每次调用8次process()的主循环。
针对大数据集使用展开循环可以节省很多时间,但对于小数据集,额外的开销则可能得不偿失。它是要花更多的代码来完成同样的任务,如果处理的不是大数据集,一般来说不值得。
4.避免双重解释
当JavaScript代码想解析KavaScript的时候就会存在双重解释惩罚。当使用eval()函数或者是Function构造函数以及使用setTimeout()传一个字符串参数时都会发生这种情况。
//某些代码求值——避免!! eval("alert('Hello world!')"); //创建新函数——避免!! var sayHi = new Function("alert('Hello world!')"); //设置超时——避免!! setTimeout("alert('Hello world!')", 500);
在以上这些例子中,都要解析包含了JavaScript代码的字符串。这个操作是不能在初始的解析过程中完成的,因为代码是包含在字符串中的,也就是说在JavaScript代码运行的同时必须新启动一个解析器来解析新的代码。实例化一个新的解析器有不容忽视的开销,所以这种代码要比直接解析慢得多。
//已修正 alert('Hello world!'); //创建新函数——已修正 var sayHi = function(){ alert('Hello world!'); }; //设置一个超时——已修正 setTimeout(function(){ alert('Hello world!'); }, 500);
如果要提高代码性能,尽可能避免出现需要按照JavaScript解析的字符串。
5.性能的其他注意事项
(1)原生方法较快
(2)Switch语句较快
(3)位运算符较快
三. 最小化语句数
1.多个变量声明
//4个语句——很浪费 var count = 5; var color = "blue"; var values = [1,2,3]; var now = new Date(); //一个语句 var count = 5, color = "blue", values = [1,2,3], now = new Date();
2.插入迭代值
当使用迭代值的时候,尽可能合并语句。
var name = values[i]; i++;
前面这2句语句各只有一个目的:第一个从values数组中获取值,然后存储在name中;第二个给变量i增加1.这两句可以通过迭代值插入第一个语句组合成一个语句。
var name = values[i++];
3.使用数组和对象字面量
//用4个语句创建和初始化数组——浪费 var values = new Array(); values[0] = 123; values[1] = 456; values[2] = 789; //用4个语句创建和初始化对象——浪费 var person = new Object(); person.name = "Nicholas"; person.age = 29; person.sayName = function(){ alert(this.name); };
这段代码中,只创建和初始化了一个数组和一个对象。各用了4个语句:一个调用构造函数,其他3个分配数据。其实可以很容易地转换成使用字面量的形式。
//只有一条语句创建和初始化数组 var values = [13,456,789]; //只有一条语句创建和初始化对象 var person = { name : "Nicholas", age : 29, sayName : function(){ alert(this.name); } };
重写后的代码只包含两条语句,减少了75%的语句量,在包含成千上万行JavaScript的代码库中,这些优化的价值更大。
只要有可能,尽量使用数组和对象的字面量表达方式来消除不必要的语句。
四 .优化DOM交互
1.最小化现场更新
一旦你需要访问的DOM部分是已经显示的页面的一部分,那么你就是在进行一个现场更新。现场更新进行得越多,代码完成执行所花的事件就越长。
var list = document.getElementById('myList'), item, i; for (var i = 0; i < 10; i++) { item = document.createElement("li"); list.appendChild(item); item.appendChild(document.createTextNode("Item" + i)); }
这段代码为列表添加了10个项目。添加每个项目时,都有2个现场更新:一个添加li元素,另一个给它添加文本节点。这样添加10个项目,这个操作总共要完成20个现场更新。
var list = document.getElementById('myList'), fragment = document.createDocumentFragment(), item, i; for (var i = 0; i < 10; i++) { item = document.createElement("li"); fragment.appendChild(item); item.appendChild(document.createTextNode("Item" + i)); } list.appendChild(fragment);
在这个例子中只有一次现场更新,它发生在所有项目都创建好之后。文档片段用作一个临时的占位符,放置新创建的项目。当给appendChild()传入文档片段时,只有片段中的子节点被添加到目标,片段本身不会被添加的。
一旦需要更新DOM,请考虑使用文档片段来构建DOM结构,然后再将其添加到现存的文档中。
2.使用innerHTML
有两种在页面上创建DOM节点的方法:使用诸如createElement()和appendChild()之类的DOM方法,以及使用innerHTML。对于小的DOM更改而言,两种方法效率都差不多。然而,对于大的DOM更改,使用innerHTML要比使用标准DOM方法创建同样的DOM结构快得多。
当把innerHTML设置为某个值时,后台会创建一个HTML解析器,然后使用内部的DOM调用来创建DOM结构,而非基于JavaScript的DOM调用。由于内部方法是编译好的而非解释执行的,所以执行快得多。
var list = document.getElementById("myList"); html = ""; i; for (i=0; i < 10; i++){ html += "<li>Item " + i +"</li>"; } list.innerHTML = html;
使用innerHTML的关键在于(和其他的DOM操作一样)最小化调用它的次数。
var list = document.getElementById("myList"); i; for (i=0; i < 10; i++){ list.innerHTML += "<li>Item " + i +"</li>"; //避免!!! }
这段代码的问题在于每次循环都要调用innerHTML,这是极其低效的。调用innerHTML实际上就是一次现场更新。构建好一个字符串然后一次性调用innerHTML要比调用innerHTML多次快得多。
3.使用事件代理(根据第13章的概念,我认为此处应为“事件委托”更为妥当)
4.注意HTMLCollection
任何时候要访问HTMLCollection,不管它是一个属性还是一个方法,都是在文档上进行一个查询,这个查询开销很昂贵。
var images = document.getElementsByTagName("img"), image, i,len; for (i=0, len=images.length; i < len; i++){ image = images[i]; //处理 }
将length和当前引用的images[i]存入变量,这样就可以最小化对他们的访问。发生以下情况时会返回HTMLCollection对象:
进行了对getElementsByTagName()的调用;
获取了元素的childNodes属性;
获取了元素的attributes属性;
访问了特殊的集合,如document.forms、document.images等。