首先搓译,性能優(yōu)化分好幾個(gè)方面悲柱,本章我們從js方面來(lái)優(yōu)化锋喜。
1:垃圾收集
日常中的某些情況下垃圾收集器無(wú)法回收無(wú)用變量些己,導(dǎo)致的一個(gè)結(jié)果就是——內(nèi)存使用率不斷增高,以下為對(duì)應(yīng)的情況以及處理方法嘿般。
①對(duì)象相互引用會(huì)導(dǎo)致引用計(jì)數(shù)始終為2怒详,所以用完對(duì)象后應(yīng)將引用設(shè)為null抖格,例子如下
letelement = document.getElementById("test");letmyObject =new Object();
myObject.element = element;
element.someObject = myObject;
//....用完后需要加如下代碼
myObject.element = null;
element.someObject = null;
②當(dāng)數(shù)據(jù)不再有用時(shí),需要通過(guò)將值設(shè)為null來(lái)解除引用,該做法適用于大多數(shù)全局變量和全局對(duì)象屬性翁脆,例子如下
function createPerson(name){
? ? let localPerson =new Object();
? ? localPerson.name = name;
? ? return localPerson
}
let globalPerson = createPerson("test")//...用完后手動(dòng)解除globalPerson =null
③關(guān)于與閉包相關(guān)的內(nèi)存泄漏如下
function assignHandler(){
? let element = document.getElementById("test");
? element.onclick =function(){
? ? alert(element.id)? ?
? }? ? ? ? ?
}//以上會(huì)導(dǎo)致element的引用數(shù)無(wú)法被回收,更改如下function assignHandler(){
? let element = document.getElementById("test");
? let id = element.id;
? element.onclick =function(){
? ? alert(id)
? }? ? ? ? ?
? element =null;?
}
2:事件委托
在js中,添加到頁(yè)面上的事件處理程序數(shù)量會(huì)直接關(guān)系到頁(yè)面整體運(yùn)行運(yùn)行性能。導(dǎo)致這一問(wèn)題的原因是多方面的派任。首先函數(shù)都是對(duì)象,都會(huì)占用內(nèi)存璧南;內(nèi)存中對(duì)象越多掌逛,性能就越差。其次司倚,必須事先指定所有事件處理程序而導(dǎo)致的DOM訪(fǎng)問(wèn)次數(shù)豆混,會(huì)延遲整個(gè)頁(yè)面的交互就緒時(shí)間。以下為對(duì)應(yīng)的情況以及處理方法
①同類(lèi)型的事件處理函數(shù)過(guò)多時(shí)动知,應(yīng)該結(jié)合為一個(gè)皿伺,例子如下:
//html代碼
- ? ?
- Go somewhere ? ?
- Say hi
let item1 = document.getElementById("goSomeWhere");
let item2 = document.getElementById("sayHi");
EventUtil.addHandler(item1, "click",function(event){
? console.log("goSomeWhere")?
}
EventUtil.addHandler(item2, "click",function(event){
? console.log("sayHi");?
}//改善點(diǎn)即將click事件結(jié)合在一起let list = document.getElementById("myLinks")
EventUtil.addHandler(list, "click",function(event){
? event = EventUtil.getEvent(event);
? let target = EventUtil.getTarget(event);
? switch(target.id){
? ? case"goSomeWhere":
? ? ? console.log("goSomeWhere");
? ? ? break;
? case"sayHi":
? ? ? console.log("sayHi");? ?
? ? ? break;? ? ? ? ? ?
? }? ?
}
②內(nèi)存留有過(guò)時(shí)不用的“空事件處理程序”也是造成性能問(wèn)題的主因,兩種情況下會(huì)造成該問(wèn)題盒粮。運(yùn)用removeChild()和replaceChild()方法去除節(jié)點(diǎn)時(shí)鸵鸥;在使用innerHTML替換頁(yè)面某一部分時(shí),如果帶有事件處理程序的元素被innerHTML刪除了拆讯,那么原有事件處理函數(shù)極有可能無(wú)法被回收脂男,例子如下
//例子中id為myBtn的點(diǎn)擊事件變?yōu)榱丝帐录幚沓绦? ? let btn = document.getElementById("myBtn");
? ? btn.onclick =function(){
? ? ? document.getElementById("myDiv").innerHTML = "xxxx";?
? ? };//改善點(diǎn)即需要手工移除事件處理程序? ? let btn = document.getElementById("myBtn");
? ? btn.onclick =function(){
? ? ? btn.onclick =null;
? ? ? document.getElementById("myDiv").innerHTML = "xxxx";?
? ? };
3:注意作用域
關(guān)于作用域鏈,我們明白訪(fǎng)問(wèn)全局變量會(huì)比訪(fǎng)問(wèn)局部變量要慢
①若某處循環(huán)使用全局變量時(shí)种呐,我們可以略做修改宰翅,例子如下
//假設(shè)有多個(gè)img標(biāo)簽的內(nèi)容,循環(huán)中引用了多次document全局變量function updateUI(){
? let imgs = document.getElementsByTagName("img")
? for(let i = 0; len = imgs.length; i < len; ++i){
? ? imgs[i].title = document.title + " image “ + i?
? }? ?
? let msg = document.getElementById("msg");
? msg.innerHTML = "Update";? ?
}
//改善點(diǎn)
function updateUI(){
? let doc = document
? let imgs = doc.getElementsByTagName("img")
? for (let i = 0; len = imgs.length; i < len; ++i){
? ? imgs[i].title = doc.title + " image “ + i?
? }? ?
? let msg = doc.getElementById("msg");
? msg.innerHTML = "Update";? ?
}
②盡量少用with爽室,因?yàn)閣ith會(huì)增加其中執(zhí)行代碼的作用域鏈的長(zhǎng)度
4:選擇正確方法
首先汁讼,我們要了解JS中算法的復(fù)雜度
標(biāo)記名稱(chēng)?描述
O(1)常數(shù)不管有多少值,執(zhí)行的時(shí)間都是恒定的阔墩。一般表示簡(jiǎn)單值和存儲(chǔ)在變量中的值
O(log n)對(duì)數(shù)總的執(zhí)行時(shí)間和值的數(shù)量相關(guān)嘿架,但是要完成算法并不一定要獲取每個(gè)值。例如:二分查詢(xún)
O(n) 線(xiàn)性總執(zhí)行時(shí)間和值的數(shù)量直接相關(guān)啸箫。例如:遍歷某個(gè)數(shù)組中的所有元素
O(n^2)平方總執(zhí)行時(shí)間和值的數(shù)量有關(guān)耸彪,每個(gè)值至少要獲取n次。例如:插入排序
常數(shù)值和訪(fǎng)問(wèn)數(shù)組元素操作都是O(1)操作忘苛;對(duì)象屬性查找操作是O(n)操作蝉娜;
如
let values =? [5, 10]; let sum = values[0] + values[1]屬于O(1)操作;let values = window.location.href屬于O(2)操作
①遇到有多次屬性查詢(xún)的場(chǎng)合扎唾,可以考慮是否能做優(yōu)化召川,例子如下
//這里總共做了6次屬性查詢(xún),其中window.location.href.substring與window.location.href.indexOf分別為3次let query = window.location.href.subsring(window.location.href.indexOf("?"))//改善, 第一次訪(fǎng)問(wèn)時(shí)復(fù)雜度會(huì)是O(n),但該版本只有4次屬性查詢(xún)胸遇,相對(duì)于原始版本節(jié)省了33%let url = window.location.href;
let query = url.substring(url.indexOf("?"));
②循環(huán)優(yōu)化荧呐,這里其實(shí)用后測(cè)試循環(huán)代替前測(cè)試循環(huán)會(huì)更好,不過(guò)本地不采用,例子如下
//原有復(fù)雜度為O(n)for(let i = 0; i < values.length; ++i){
? process(values[i]);?
}//更改后復(fù)雜度為O(1)for(let i = values.length - 1; i >= 0; --i){
? process(values[i])?
}
③最小化語(yǔ)句數(shù)相關(guān)
例如進(jìn)行多個(gè)聲明時(shí)倍阐,我們可以進(jìn)行組合概疆,例子如下
//多個(gè)聲明
let count = 5;
let color = "blue";
let values = [1, 2, 3];//組合成一個(gè)let count = 5峰搪,
? ? color = ”blue",
? ? values = [1, 2, 3]
例如插入迭代值時(shí)届案,例子如下
//修改前
let name = values[i];
i++;//修改后let name = values[i++]
使用數(shù)組和對(duì)象字面量時(shí),例子如下
//修改前
let values =new Array();
values[0] = 123;
values[1] = 456;
values[2] = 789;
let person =new Object();
person.name = "Eric";
person.age = 20;//修改后let values = [123, 456, 789]
let person = {
? name: "Eric",
? age:20,? ?
}
④創(chuàng)建DOM節(jié)點(diǎn)最好使用innerHTML方法罢艾,因?yàn)閕nnerHTML設(shè)置值時(shí)楣颠,后臺(tái)會(huì)創(chuàng)建HTML解析器,然后使用內(nèi)部的DOM調(diào)用來(lái)創(chuàng)建DOM結(jié)構(gòu)咐蚯,而非基于JS的DOM調(diào)用童漩。
調(diào)用一次innerHTML,就會(huì)進(jìn)行一次現(xiàn)場(chǎng)刷新春锋,循環(huán)插入DOM結(jié)構(gòu)時(shí)矫膨,應(yīng)注意盡量調(diào)用少次數(shù)的innerHTML,代碼如下
//錯(cuò)誤方法期奔,做了很多次現(xiàn)場(chǎng)刷新
let list = document.getElementById("myList"),
? ? i;for(i = 0; i < 10; ++i){
? list.innerHTML = html+= "
Item " + i + " "?}//正確方法侧馅,盡管在字符串連接上有性能損失,但卻只做了一次現(xiàn)場(chǎng)刷新let list = document.getElementById("myList"),
? ? html = "",
? ? i;for(i = 0; i < 10; ++i){
? html += "
Item " + i + " "?}
list.innerHTML = html
⑤其他如有多個(gè)if-else語(yǔ)句時(shí)呐萌,應(yīng)盡可能轉(zhuǎn)為Switch語(yǔ)句馁痴;用appendChild()插入元素時(shí),應(yīng)采用自上而下插入肺孤;面向?qū)ο缶幊虝r(shí)罗晕,應(yīng)合理釋放內(nèi)存,設(shè)object為null赠堵。