摘要:可維護(hù)解耦采用引入文件的方式取代在頁(yè)面寫(xiě)代碼避免在中創(chuàng)建大量當(dāng)用于插入數(shù)據(jù)時(shí),盡量不要直接插入標(biāo)記。簡(jiǎn)化循環(huán)體循環(huán)體是執(zhí)行最多的,所以要確保其被最大限地優(yōu)化,確保沒(méi)有某些可以被很容易移除循環(huán)的密集計(jì)算。
可維護(hù) 解耦HTML/JavaScript
1、采用引入js文件的方式取代在html頁(yè)面寫(xiě)js代碼
2、避免在js中創(chuàng)建大量html
(1)當(dāng)js用于插入數(shù)據(jù)時(shí),盡量不要直接插入標(biāo)記。一般可以在頁(yè)面中直接包含并隱藏標(biāo)記,然后等到整個(gè)頁(yè)面渲染好之后,就可以用js顯示該標(biāo)記,而非生成它
(2)也可以通過(guò)Ajax請(qǐng)求獲得更多要顯示的html,這個(gè)方法可以讓同樣的渲染層(PHP、JSP、Ruby等等)來(lái)輸出標(biāo)記,而不是直接嵌在js中
盡量不要在js中更改樣式,而是采用動(dòng)態(tài)更改樣式類,從而做到對(duì)于樣式的問(wèn)題應(yīng)只查看CSS文件來(lái)解決,例:
element.style.color = "red"; element.style.backgroundColor = "blue"; 更改后: element.className = "edit";解耦應(yīng)用邏輯/事件處理程序
每個(gè)web應(yīng)用一般都有相當(dāng)多的事件處理程序,監(jiān)聽(tīng)這無(wú)數(shù)不同的事件,然而,很少有能仔細(xì)得將應(yīng)用邏輯從事件處理程序中分離的,如下:
function handleKeyPress (event) { if(event.keyCode == 1){ let target = event; let value = 5 * parseInt(target.value); if(value > 10){ document.getElementById("tip-msg").style.display = "block"; } } }
這個(gè)事件處理程序處理包含了應(yīng)用邏輯,還進(jìn)行了事件處理,這種方式的問(wèn)題有其雙重性
(1)、處理通過(guò)事件之外就沒(méi)有辦法執(zhí)行應(yīng)用邏輯
(2)、調(diào)試?yán)щy,如果沒(méi)有發(fā)生預(yù)想的結(jié)果,并不知道是事件沒(méi)被調(diào)用還是應(yīng)用邏輯失敗
(3)、如果后續(xù)的事件需要執(zhí)行相同的應(yīng)用邏輯,那么就必須復(fù)制功能代碼或?qū)⒋a抽到一個(gè)多帶帶的函數(shù)中所以就好進(jìn)行兩者的解耦,即一個(gè)事件處理程序應(yīng)該從事件對(duì)象中提取相關(guān)信息,并將這些信息傳送到處理應(yīng)用邏輯的某個(gè)方法中,前面例子重寫(xiě)如下:
function validateValue (value) { value = 5 * parseInt(value); if(value > 10){ document.getElementById("tip-msg").style.display = "block"; } } function handleKeyPress (event) { if(event.keyCode == 13){ let target = event; validateValue(target.value); } }
從而使validateValue()中沒(méi)有任何東西會(huì)依賴于任何事件處理程序邏輯,他只接收一個(gè)值,并根據(jù)該值進(jìn)行其他處理
好處:如果事件最初只有鼠標(biāo)事件觸發(fā),那么現(xiàn)在只需少量修改就可以實(shí)現(xiàn)按鍵觸發(fā)
最多創(chuàng)建一個(gè)全局變量,讓其他對(duì)象和函數(shù)存在其中,如:
//兩個(gè)全局變量——避免??! let name= "bad"; function sayName() { alert(name); } //一個(gè)全局變量——推薦 let good = { name: "nice", sayName: function () { alert(this.name); } }
多人協(xié)作開(kāi)發(fā)可以使用命名空間
//創(chuàng)建全局對(duì)象 let wrox = {}; //為T(mén)ony創(chuàng)建命名空間 wrox.Tony = {}; //為T(mén)ony(可以以人名劃分)創(chuàng)建方法 wrox.Tony.sayName = function () { ... };
上述例子,wrox是全局變量,其他命名空間在此之上創(chuàng)建,只要所有人都按這樣寫(xiě),那么就不用當(dāng)心不同開(kāi)發(fā)者創(chuàng)建相同的方法等,因?yàn)樗嬖谟诓煌拿臻g
避免與null進(jìn)行比較應(yīng)該讓條件與預(yù)想的類型進(jìn)行比較而不是與null
//bad function sortArray(values) { if(values != null){ ... } } //good function sortArray(values) { if(values instanceof Array){ ... } }抽離常量
const constants = { INVALID_VALUE_MSG:"Invalid value!", INVALID_VALUE_URL:"/errors/invalid.php" }性能優(yōu)化
因?yàn)樵L問(wèn)全局變量要比訪問(wèn)局部變量慢,因?yàn)橐闅v作用域鏈,所以減少花費(fèi)在作用域鏈上的時(shí)間,就可以增加腳本的整體新能
避免全局查找//bad function updateUI () { let imgs = document.getElementsByTagName("img"); for(let i=0, len=imgs.length; i使用事件代理上面將document對(duì)象保存在doc變量中,然后替換原來(lái)的document,與原來(lái)相比,只需進(jìn)行一次全局查找,速度肯定更快
循環(huán)優(yōu)化循環(huán)優(yōu)化基本步驟如下:
1、減值迭代——大多數(shù)循環(huán)使用一個(gè)從0開(kāi)始、增加到某個(gè)特定值的迭代器。在很多情況下,從最大值開(kāi)始,在循環(huán)中的迭代器更加高效。
2、簡(jiǎn)化終止條件——由于每次循環(huán)過(guò)程都會(huì)計(jì)算終止條件,所以必須保證它盡可能快。也就是說(shuō)避免屬性查找或者其他O(n)的操作。
3、簡(jiǎn)化循環(huán)體——循環(huán)體是執(zhí)行最多的,所以要確保其被最大限地優(yōu)化,確保沒(méi)有某些可以被很容易移除循環(huán)的密集計(jì)算。
4、使用后側(cè)試循環(huán)——最常用的for循環(huán)和while循環(huán)都是前測(cè)試循環(huán)。而入do—while這種后側(cè)試循環(huán),可以避免最初終止條件的計(jì)算//基本for循環(huán) for(let i=0;i最小語(yǔ)句數(shù)=0; i--){ process(values[i]); } //再進(jìn)行改造成后測(cè)試循環(huán),此處主要的優(yōu)化是將終止條件和自減操作符組合成了單個(gè)語(yǔ)句 let i = values.length - 1; if(i> -1){ do{ process(valeus[i]); }while(--i>=0) } 1、多個(gè)聲明變量
//bad let count = 5; let color = "blue"; let values = [1,2,3]; let now = new Date(); //good let count = 5, color = "blue", values = [1,2,3], now = new Date();2、插入迭代值
//bad let name = values[i]; i++; //good let name = values[i++];3、使用數(shù)組和對(duì)象字面量
//bad let values = new Array(); values[0] = 123; values[1] = 456; values[2] = 789; //bad let person = new Object(); person.name = "Tony"; person.age = 18; person.sayName = function () { alert(this.name); } //good let values = [123,456,789]; let person = { name: "Tony", age: 18, sayName: function () { alert(this.name); } }優(yōu)化DOM交互1、最小現(xiàn)場(chǎng)更新
//bad let list = document.getElementById("myList"), item, i; for(i=0; i<10; i++){ item = document.createElement("li"); list = appendChilde(item); item.appendChild(document.createTextNode("Item" + i); }上述代碼為列表添加了10個(gè)項(xiàng)目,每添加個(gè)項(xiàng)目時(shí),都有2個(gè)現(xiàn)場(chǎng)更新:一個(gè)添加元素,另一個(gè)給他添加文本節(jié)點(diǎn),這樣添加10個(gè)項(xiàng)目,這個(gè)操作總共要完成20個(gè)現(xiàn)場(chǎng)更新。
解決方法:
1、將列表從頁(yè)面上移除,最后進(jìn)行更新,最后在將列表插回到同樣的位置,看似很美好,但這樣做會(huì)導(dǎo)致在每次更新的時(shí)候它會(huì)不必要的閃爍
2、使用文檔碎片來(lái)構(gòu)建DOM結(jié)構(gòu),接著將其添加到list元素中,這個(gè)方式避免了閃爍,如下://good,只有一次現(xiàn)場(chǎng)更新 let list = document.getElementById("myList"); framgent = document.createDocumentFragment(), itme, i; for(i=0; i<10; i++){ item = document.createElement("li"); fragment.appendChild(item); item.appendChild(document.createTextNode("Item" + i)); } list.appendChild(fragment);2、使用innerHTML
有兩種方式在頁(yè)面上創(chuàng)建DOM節(jié)點(diǎn):使用諸如createElement()和appendChild()之類的DOM方法,以及使用innerHTML。對(duì)于小的DOM而言,兩者效率差不多,對(duì)于大的DOM改動(dòng),使用innerHTML要快得多。
原因:當(dāng)把innerHTML設(shè)置為某個(gè)值時(shí),后臺(tái)會(huì)創(chuàng)建一個(gè)HTML解析器,然后使用內(nèi)部的DOM調(diào)用來(lái)創(chuàng)建DOM結(jié)構(gòu),而非基于js的DOM調(diào)用,由于內(nèi)部方法是編譯好的而非解釋執(zhí)行的,所以執(zhí)行快得多,所以上面例子還可以優(yōu)化let list = document.getElementById("myList"), html = "", i; for(i=0; i<10; i++){ html += `Item${i} `; } list.innerHTML = html; //tip同樣要避免多次調(diào)用innerHTML,即要做到構(gòu)建好一個(gè)字符串然后一次性調(diào)用innerHTML
頁(yè)面上的事件處理程序的數(shù)量和頁(yè)面的響應(yīng)用戶交互的速度之間是負(fù)相關(guān)的
任何冒泡的事件都不僅僅可以在事件的目標(biāo)上進(jìn)行處理,目標(biāo)的任何祖先節(jié)點(diǎn)上也能處理,如果可能,在文檔級(jí)別附加事件處理程序,這樣就可以處理整個(gè)頁(yè)面的事件
任何時(shí)候要訪問(wèn)HTMLCollection,不管是一個(gè)屬性還是一個(gè)方法,都是在文檔上進(jìn)行的一個(gè)查詢,這個(gè)查詢開(kāi)銷很昂貴,爾等消費(fèi)不起
let images = document.getElementsByTagName("img"); imags, i, len; for(i=0; len=images.length; itip:發(fā)生以下情況會(huì)返回HTMLCollection對(duì)象:
展開(kāi)循環(huán)(Duff)
(1)、進(jìn)行了對(duì)getElementsByTagName()的調(diào)用;
(2)、獲取了元素的childNodes屬性;
(3)、獲取了元素的attribute屬性;
(4)、訪問(wèn)了特殊的集合,如document.forms、document.images等當(dāng)循環(huán)的次數(shù)是確定的,消除循環(huán)并使用多次函數(shù)調(diào)用往往更快,
//low for(let i=0;i<3;i++){ process(values[i]); } //fast,消除建立循環(huán)和處理終止條件的額外開(kāi)銷 process(values[0]); process(values[1]); process(values[2]);如果迭代次數(shù)事項(xiàng)不能確定,可以使用Duff裝置的技術(shù)
Duff:通過(guò)計(jì)算迭代的次數(shù)是否為8的倍數(shù)將一個(gè)循環(huán)展開(kāi)為一系列語(yǔ)句let iterations = Math.ceil(values.length / 8); //向上取整確保結(jié)果是整數(shù) let startAt = values.length % 8; //獲取無(wú)法通過(guò)上面進(jìn)行處理的項(xiàng),如果values.length為10,那么startAt為2 let i = 0; do { switch(startAt) { case 0: process(values[i++]); case 7: process(values[i++]); case 6: process(values[i++]); case 5: process(values[i++]); case 4: process(values[i++]); case 3: process(values[i++]); case 2: process(values[i++]); case 1: process(values[i++]); } startAt = 0; } while (--iterations > 0);上面運(yùn)行的結(jié)果是先運(yùn)行startAt次的process(),然后startAt設(shè)置為0,后面循環(huán)都執(zhí)行8次process(),展開(kāi)循環(huán)可以提升大數(shù)據(jù)集的處理速度。
將do-while循環(huán)分成2個(gè)多帶帶的循環(huán)可以讓Duff更快let iterations = Math.ceil(values.length / 8); //向上取整確保結(jié)果是整數(shù) let startAt = values.length % 8; //獲取無(wú)法通過(guò)上面進(jìn)行處理的項(xiàng),如果values.length為10,那么startAt為2 let 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);上面代碼讓剩余的計(jì)算部分不會(huì)在實(shí)際循環(huán)中處理,而是在一個(gè)初始化循環(huán)中進(jìn)行除以8的操作,當(dāng)處理掉了額外的元素,繼續(xù)執(zhí)行每次調(diào)用8次process()的主循環(huán),這個(gè)方法幾乎比原始的Duff裝置快40%
其他性能優(yōu)化注意事項(xiàng)
tip:以上只適用于處理大數(shù)據(jù)集下面并非主要影響性能的主要問(wèn)題,應(yīng)用得當(dāng),會(huì)有相當(dāng)大的提升
1、原生方法較快——只要有可能,使用原生方法而不是自己用js重寫(xiě)一個(gè),因?yàn)樵椒ㄊ怯米⑷?b>C/C++之類的編譯型語(yǔ)言寫(xiě)出來(lái)的,所以要比js快得多,比如要用Math對(duì)象的數(shù)學(xué)運(yùn)算
2、Swithc語(yǔ)句較快——如果有一系列復(fù)雜的if-else語(yǔ)句,可以轉(zhuǎn)換成單個(gè)switch語(yǔ)句則可以得到更快的代碼,還可以通過(guò)將case語(yǔ)句按照最可以能的到最不可能的順序進(jìn)行組織,來(lái)進(jìn)一步優(yōu)化switch語(yǔ)句。
3、位運(yùn)算符較快——當(dāng)進(jìn)行數(shù)學(xué)運(yùn)算時(shí),位運(yùn)算符操作要比任何布爾運(yùn)算或者算數(shù)運(yùn)算快,諸如取模,邏輯與和邏輯或都可以考慮用位運(yùn)算替換以上內(nèi)容參考自《JavaScript 高級(jí)程序設(shè)計(jì)(第三版)》
終于寫(xiě)完了(~ ̄▽ ̄)~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/99157.html
摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開(kāi)發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開(kāi)發(fā)教程工程實(shí)踐深度閱讀開(kāi)源項(xiàng)目巔峰人生等欄目。對(duì)該漏洞的綜合評(píng)級(jí)為高危。目前,相關(guān)利用方式已經(jīng)在互聯(lián)網(wǎng)上公開(kāi),近期出現(xiàn)攻擊嘗試爆發(fā)的可能。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開(kāi)發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開(kāi)發(fā)教程、工程實(shí)踐、深度閱讀、開(kāi)源項(xiàng)目、巔峰人生等欄目。歡...
摘要:谷歌,,,雅虎和最近因世界杯獲得龐大觀眾數(shù)量的都在使用。因此,數(shù)據(jù)庫(kù)服務(wù)器的能力是毋庸置疑的。微軟的服務(wù)器,服務(wù)器以及未來(lái)的更新價(jià)格昂貴。更依賴于微軟數(shù)量有限的開(kāi)發(fā)者做出的改進(jìn)和更新。 【編者按】本文主要針對(duì)開(kāi)源 PHP 和非開(kāi)源的 ASP.NET 在性能、成本、可擴(kuò)展性,技術(shù)支持和復(fù)雜性等方面進(jìn)行比較。 在網(wǎng)上論壇,總是有成百上千的文章和帖子在討論 PHP 和 ASP.NET,究竟誰(shuí)...
摘要:相反,我們將專注于將添加到用編寫(xiě)的簡(jiǎn)單應(yīng)用程序中。使用創(chuàng)建應(yīng)用程序。示例應(yīng)用程序有兩個(gè)組件應(yīng)用程序和。除在全球率先支持外,現(xiàn)已全面應(yīng)用于等主流框架中。 showImg(https://segmentfault.com/img/bVbcvaQ?w=500&h=278); 概述 在本文中,我們將展示如何將WijmoJS與NPM和Webpack一起使用來(lái)創(chuàng)建最流行的基于JavaScript應(yīng)...
摘要:前端每周清單第期與模式變遷與優(yōu)化界面生成作者王下邀月熊編輯徐川前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開(kāi)發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開(kāi)發(fā)教程工程實(shí)踐深度閱讀開(kāi)源項(xiàng)目巔峰人生等欄目。 showImg(https://segmentfault.com/img/remote/1460000013279448); 前端每周清單第 51 期: React Context A...
閱讀 2408·2021-09-28 09:45
閱讀 3652·2021-09-24 09:48
閱讀 2325·2021-09-22 15:49
閱讀 3179·2021-09-08 16:10
閱讀 1658·2019-08-30 15:54
閱讀 2397·2019-08-30 15:53
閱讀 3078·2019-08-29 18:42
閱讀 2926·2019-08-29 16:19