Die Leistungsprobleme von JavaScript sind nicht zu unterschätzen, weshalb wir Entwickler beim Schreiben von JavaScript-Programmen einigen Details mehr Aufmerksamkeit schenken müssen. In diesem Artikel werden die Wissenspunkte der JavaScript-Leistungsoptimierung ausführlich vorgestellt, was auf jeden Fall nützliche Informationen ist.
Ich habe Javascript gelernt und auch „Sharp Development of Jquery Kernel Detaillierte Erklärung und Praxis“ gelesen. Es gibt nur zwei Wörter für dieses Buch: scharf, Vielleicht verstehe ich es aufgrund von Javascript nicht gründlich genug, oder was noch wichtiger ist: Ich bin nicht gut im Denken und zu faul zum Denken, sodass ich einiges im Wesentlichen nicht verstehe davon.
Da ich mich verbessern möchte und nicht in eine größere Welt eintreten kann, muss ich einen eigenen Ort finden, an dem ich gut überleben kann. Daher sammle ich normalerweise einige allgemeine Kenntnisse über die absichtliche oder unabsichtliche Verwendung von jQuery, insbesondere wenn es so ist Wenn es um Leistungsanforderungen geht, frage ich mich immer, ob es einen besseren Weg gibt, diese zu erreichen.
Im Folgenden sind einige Tipps aufgeführt, die ich nur als Referenz zusammengefasst habe. (Ich werde zuerst einen allgemeinen Titel geben, dann einen kurzen Absatz verwenden, um die Bedeutung zu erläutern, und schließlich eine Demo verwenden, um sie kurz zu erklären)
Global wird in einer Funktion verwendet Objekte werden als lokale Variablen gespeichert, um globale Suchvorgänge zu reduzieren, da der Zugriff auf lokale Variablen schneller ist als der Zugriff auf globale Variablen
function search() { //当我要使用当前页面地址和主机域名 alert(window.location.href + window.location.host); } //最好的方式是如下这样 先用一个简单变量保存起来 function search() { var location = window.location; alert(location.href + location.host); }
If Sie zielen auf kontinuierlich laufenden Code ab, sollte nicht setTimeout, sondern setInterval verwenden, da setTimeout jedes Mal einen Timer initialisiert und setInterval nur zu Beginn einen Timer initialisiert
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);
Wenn Sie mehrere Strings verketten möchten, sollten Sie less += verwenden, z. B.
s+=a;
s+=b;
s+=c;
sollte als s+=a + b + c geschrieben werden.
Und wenn Sie Zeichenfolgen sammeln Wenn Sie beispielsweise +=-Vorgänge für dieselbe Zeichenfolge mehrmals ausführen, ist es am besten, einen Cache zu verwenden, JavaScript-Arrays zum Sammeln zu verwenden und schließlich die Join-Methode zu verwenden, um sie zu verbinden
var buf = []; for (var i = 0; i < 100; i++) { buf.push(i.toString()); } var all = buf.join("");
Ähnlich wie Funktionen erstellt die with-Anweisung ihren eigenen Gültigkeitsbereich und erhöht so die Gültigkeitskette des darin ausgeführten Codes. Länge Aufgrund der zusätzlichen Bereichskettensuche ist der in der with-Anweisung ausgeführte Code definitiv langsamer als der außerhalb ausgeführte Code. Versuchen Sie, die with-Anweisung nicht zu verwenden, wenn nicht verwendet werden kann.
with (a.b.c.d) { property1 = 1; property2 = 2; } //可以替换为: var obj = a.b.c.d; obj.property1 = 1; obj.property2 = 2;
Im Allgemeinen ist es am besten, „“ + 1 zu verwenden, um Zahlen in Zeichenfolgen umzuwandeln, auch wenn es so aussieht Es sieht etwas hässlicher aus, aber tatsächlich ist dies das effizienteste, was die Leistung betrifft:
(“” +) > )
Viele Leute verwenden gerne parseInt(). Tatsächlich wird parseInt() zum Konvertieren von Zeichenfolgen in Zahlen verwendet, nicht zwischen Gleitkommazahlen und Ganzzahlen. Wenn die toString()-Methode für die Typkonvertierung definiert ist, wird empfohlen, toString() explizit aufzurufen, da die interne Operation nach dem Ausprobieren aller Möglichkeiten die toString()-Methode ausprobiert des Objekts, um zu sehen, ob es in einen String konvertiert werden kann, daher ist der direkte Aufruf dieser Methode effizienter
In JavaScript können alle Variablen mit einer einzigen var-Anweisung deklariert werden , sodass die Anweisungen kombiniert werden, um die Ausführungszeit des gesamten Skripts zu verkürzen. Genau wie der obige Code ist auch das obige Codeformat ziemlich standardisiert, sodass es auf einen Blick klar ist.
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
Direkt verwenden Mengen
Sobald Sie das DOM aktualisieren müssen, sollten Sie erwägen, Dokumentfragmente zum Erstellen des DOM zu verwenden Struktur und fügen Sie sie dann hinzu. Fügen Sie sie einem vorhandenen Dokument hinzu.
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" };
Verwenden Sie die innerHTML-Zuweisung einmal, anstatt das Dom-Element zu erstellen.
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);
Klonen Sie das Vorlagenelement anstelle von createElement
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('');
Verwenden firstChild und nextSibling anstelle von childNodes zum Durchqueren von Dom-Elementen
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);
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;
删除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;
最小化访问NodeList的次数可以极大的改进脚本的性能
var images = document.getElementsByTagName('img'); for (var i = 0, len = images.length; i < len; i++) { }
编写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++) { //…… }
简化循环体
循环体是执行最多的,所以要确保其被最大限度的优化
使用后测试循环
在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]; }
最常用的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);
if (oTest != '#ff0000') { //do something } if (oTest != null) { //do something } if (oTest != false) { //do something } //虽然这些都正确,但用逻辑非操作符来操作也有同样的效果: if (!oTest) { //do something }
将条件分支,按可能性顺序从高到低排列:可以减少解释器对条件的探测次数
在同一条件子的多(>2)条件分支时,使用switch优于if:switch分支选择的效率高于if,在IE下尤为明显。4分支的测试,IE下switch的执行时间约为if的一半。
使用三目运算符替代条件分支
if (a > b) { num = a; } else { num = b; } //可以替换为: num = a > b ? a : b;
重复值:任何在多处用到的值都应该抽取为一个常量
用户界面字符串:任何用于显示给用户的字符串,都应该抽取出来以方便国际化
URLs:在Web应用中,资源位置很容易变更,所以推荐用一个公共地方存放所有的URL
任意可能会更改的值:每当你用到字面量值的时候,你都要问一下自己这个值在未来是不是会变化,如果答案是“是”,那么这个值就应该被提取出来作为一个常量。
由于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 }; };
因为JavaScript可以在任何时候修改任意对象,这样就可以以不可预计的方式覆写默认的行为,所以如果你不负责维护某个对象,它的对象或者它的方法,那么你就不要对它进行修改,具体一点就是说:
不要为实例或原型添加属性
不要为实例或者原型添加方法
不要重定义已经存在的方法
不要重复定义其它团队成员已经实现的方法,永远不要修改不是由你所有的对象,你可以通过以下方式为对象创建新的功能:
创建包含所需功能的新对象,并用它与相关对象进行交互
创建自定义类型,继承需要进行修改的类型,然后可以为自定义类型添加额外功能
如果循环引用中包含DOM对象或者ActiveX对象,那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前,即使是刷新页面,这部分内存不会被浏览器释放。
简单的循环引用:
var el = document.getElementById('MyElement'); var func = function () { //… } el.func = func; func.element = el;
但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。
function init() { var el = document.getElementById('MyElement'); el.onclick = function () { //…… } } init();
init在执行的时候,当前上下文我们叫做context。这个时候,context引用了el,el引用了function,function引用了context。这时候形成了一个循环引用。
下面2种方法可以解决循环引用:
function init() { var el = document.getElementById('MyElement'); el.onclick = function () { //…… } } init(); //可以替换为: function init() { var el = document.getElementById('MyElement'); el.onclick = function () { //…… } el = null; } init();
将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();
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();
把function抽到新的context中,这样,function的context就不包含对el的引用,从而打断循环引用。
IE下,脚本创建的dom对象,如果没有append到页面中,刷新页面,这部分内存是不会回收的!
function create() { var gc = document.getElementById('GC'); for (var i = 0; i < 5000; i++) { var el = document.createElement('p'); el.innerHTML = "test"; //下面这句可以注释掉,看看浏览器在任务管理器中,点击按钮然后刷新后的内存变化 gc.appendChild(el); } }
将dom元素的innerHTML设置为空字符串,可以释放其子元素占用的内存。
在rich应用中,用户也许会在一个页面上停留很长时间,可以使用该方法释放积累得越来越多的dom元素使用的内存。
在rich应用中,随着实例化对象数量的增加,内存消耗会越来越大。所以应当及时释放对对象的引用,让GC能够回收这些内存控件。
对象:obj = null
对象属性:delete obj.myproperty
数组item:使用数组的splice方法释放数组中不用的item
对string的方法调用,比如’xxx’.length,浏览器会进行一个隐式的装箱操作,将字符串先转换成一个String对象。推荐对声明有可能使用String实例方法的字符串时,采用如下写法:
var myString = new String(‘Hello World’);
1、解耦HTML/JavaScript
JavaScript和HTML的紧密耦合:直接写在HTML中的JavaScript、使用包含内联代码的