這篇文章給大家分享的是有關(guān)JavaScript代碼覆蓋的示例分析的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
成都創(chuàng)新互聯(lián)專注于網(wǎng)站建設(shè)|成都網(wǎng)站維護|優(yōu)化|托管以及網(wǎng)絡(luò)推廣,積累了大量的網(wǎng)站設(shè)計與制作經(jīng)驗,為許多企業(yè)提供了網(wǎng)站定制設(shè)計服務(wù),案例作品覆蓋格柵板等行業(yè)。能根據(jù)企業(yè)所處的行業(yè)與銷售的產(chǎn)品,結(jié)合品牌形象的塑造,量身開發(fā)品質(zhì)網(wǎng)站。
它為什么是有用的?
作為一名JavaScript開發(fā)者,你可能經(jīng)常發(fā)現(xiàn)自己處于代碼覆蓋可能有用的情景。例如:
對測試套件的質(zhì)量感興趣? 重構(gòu)一個大型的遺留項目? 代碼覆蓋可以準(zhǔn)確顯示代碼庫中已覆蓋了哪些部分。
想快速了解是否覆蓋了代碼庫的特定部分? 代碼覆蓋可以顯示有關(guān)應(yīng)用程序的哪些部分已被執(zhí)行的實時信息,而不是使用console.log進行printf-風(fēng)格的調(diào)試或手動執(zhí)行代碼。
或者你可能正在優(yōu)化速度,并想知道要關(guān)注哪些點? 執(zhí)行次數(shù)可以指出關(guān)鍵函數(shù)和循環(huán)。
JavaScript在V8中的代碼覆蓋
今年早些時候,我們在V8上添加了對JavaScript代碼覆蓋的原生支持。5.9版本中的初始發(fā)布提供了函數(shù)粒度(顯示已執(zhí)行的函數(shù))的覆蓋范圍,后來擴展為支持在v6.2中的塊粒度覆蓋(同樣的,僅對于單獨表達式有效)。
函數(shù)粒度(左側(cè))和塊粒度(右側(cè))
對JavaScript開發(fā)者
目前訪問覆蓋信息有兩種主要的方式。對于JavaScript開發(fā)者,Chrome DevTools的Coverage tab給出了JS (和CSS)覆蓋率并在源碼面板中指出了無用代碼。
塊覆蓋coverage 在DevTools Coverage 面板中的塊覆蓋。覆蓋的行使用綠色標(biāo)注,未覆蓋的行則使用紅色。
基于V8覆蓋數(shù)據(jù)的Istanbul.js報告
給嵌入式
嵌入式及框架作者可以通過直接hook到Inspector API上獲得更大的靈活性。V8提供兩種不同的覆蓋模式:
1.盡力覆蓋模式下收集覆蓋信息,確保在運行時對性能的影響最小,但可能會丟失已被垃圾回收(GC)函數(shù)的數(shù)據(jù)。
2.精確覆蓋確保不會因為GC而丟失任何數(shù)據(jù),用戶可以選擇接收執(zhí)行計數(shù)而不是二進制覆蓋信息;但性能可能會受此額外開銷的影響(有關(guān)詳細(xì)信息,請參閱下一節(jié))。精準(zhǔn)覆蓋可以按函數(shù)或塊粒度收集信息。
精準(zhǔn)覆蓋的Inspector API如下:
Profiler.startPreciseCoverage(callCount, detailed) 使能覆蓋信息收集,可選調(diào)用次數(shù)(vs.二進制覆蓋)以及塊粒度(vs. 函數(shù)粒度);
Profiler.takePreciseCoverage() 返回已收集的覆蓋信息,其中包含源碼范圍列表以及相關(guān)的執(zhí)行次數(shù);
Profiler.stopPreciseCoverage() 禁用收集并釋放相關(guān)數(shù)據(jù)結(jié)構(gòu)。
Inspector協(xié)議間的通信可能如下所示:
// The embedder directs V8 to begin collecting precise coverage. { "id": 26, "method": "Profiler.startPreciseCoverage", "params": { "callCount": false, "detailed": true }} // Embedder requests coverage data (delta since last request). { "id": 32, "method":"Profiler.takePreciseCoverage" } // The reply contains collection of nested source ranges. { "id": 32, "result": { "result": [{ "functions": [ { "functionName": "fib", "isBlockCoverage": true, // Block granularity. "ranges": [ // An array of nested ranges. { "startOffset": 50, // Byte offset, inclusive. "endOffset": 224, // Byte offset, exclusive. "count": 1 }, { "startOffset": 97, "endOffset": 107, "count": 0 }, { "startOffset": 134, "endOffset": 144, "count": 0 }, { "startOffset": 192, "endOffset": 223, "count": 0 }, ]}, "scriptId": "199", "url": "file:///coverage-fib.html" } ] }} // Finally, the embedder directs V8 to end collection and // free related data structures. {"id":37,"method":"Profiler.stopPreciseCoverage"}
同理,盡力覆蓋可以使用 Profiler.getBestEffortCoverage() 。
幕后細(xì)節(jié)
如上一節(jié)所述,V8支持兩種主要的代碼覆蓋模式:盡力和精確覆蓋。欲了解他們實現(xiàn)概述,請繼續(xù)閱讀。
盡力覆蓋
盡力和精確覆蓋模式都大量重用其它的V8機制,其中首數(shù)被稱為調(diào)用計數(shù)器的機制。每次通過V8的Ignition解釋器調(diào)用函數(shù)時,我們都會在函數(shù)的反饋向量上增加其調(diào)用計數(shù)器。隨著函數(shù)后來變得愈加頻繁并通過優(yōu)化編譯器做了提升,這個計數(shù)器用于幫助輔助關(guān)于內(nèi)聯(lián)函數(shù)的內(nèi)聯(lián)決策;現(xiàn)在,我們也依靠它報告代碼覆蓋情況。
第二種重用機制確立了函數(shù)的源碼范圍。報告代碼覆蓋時,調(diào)用計數(shù)需要與源文件中的相關(guān)范圍作關(guān)聯(lián)。例如,在下面的示例中,我們不僅需要報告函數(shù)f已經(jīng)執(zhí)行了一次,還包含f的源碼范圍從第1行開始到第3行結(jié)束。
function f() { console.log('Hello World'); } f();
又一次我們是幸運的,我們能夠重用 V8 中的現(xiàn)有信息。由于 Function.prototype.toString 需要知道函數(shù)在原文件中的位置以提取適當(dāng)?shù)淖幼址?,函?shù)已經(jīng)知道它們在源代碼中的起始位置和結(jié)束位置。
在收集到最優(yōu)的覆蓋范圍時,這兩種機制簡單地結(jié)合在一起:首先,我們通過遍歷整個堆來找到所有存活的函數(shù)。對于每個可見的函數(shù),我們報告調(diào)用次數(shù)(存儲在反饋向量中,我們可以從函數(shù)中訪問)和源范圍(方便存儲在函數(shù)本身)。
請注意,由于無論是否啟用 coverage,都會維護調(diào)用計數(shù),因此盡力服務(wù)的覆蓋不會引入任何運行時開銷。它也不使用專用的數(shù)據(jù)結(jié)構(gòu),因此既不需要顯式啟用也無需顯式禁用。
那么為什么這種模式稱為盡力服務(wù)(best-effort)呢,它的局限性是什么? 超出范圍的函數(shù)可能會被垃圾回收器釋放掉。這意味著相關(guān)的調(diào)用計數(shù)將會丟失,事實上我們完全忘記了這些函數(shù)曾經(jīng)存在過。 因此“盡力服務(wù)”:即使我們盡力了,所收集的覆蓋信息也可能不完整。
精準(zhǔn)覆蓋 (函數(shù)粒度)
與盡力服務(wù)模式相比,精確覆蓋可確保所提供的覆蓋信息是完整的。為實現(xiàn)這一目標(biāo),我們會在啟用精準(zhǔn)覆蓋后將所有反饋向量添加到V8的根參考集中,從而阻止GC對其進行回收。雖然這確保了信息無丟失,但它通過人為地保持對象存活增加內(nèi)存開銷。
精準(zhǔn)覆蓋模式還可以提供執(zhí)行計數(shù)。這為精準(zhǔn)覆蓋實施增加了另一個竅門。回想一下,每次通過V的解釋器調(diào)用函數(shù)時,調(diào)用計數(shù)器都會遞增,并且一旦函數(shù)訪問頻率過高,這些函數(shù)就可以升級并進行優(yōu)化。 但優(yōu)化的函數(shù)不再增加其調(diào)用計數(shù)器,因此必須禁用優(yōu)化編譯器,以使其報告的執(zhí)行次數(shù)保持準(zhǔn)確。
精準(zhǔn)覆蓋(塊粒度)
塊粒度覆蓋必須報告準(zhǔn)確到獨立表達式層級的覆蓋范圍。例如,在下面的一段代碼中,塊覆蓋可以檢測到條件表達式的else分支: c從不執(zhí)行,而函數(shù)粒度覆蓋只會知道函數(shù) f(作為一個整體)被覆蓋了。
function f(a) { return a ? b : c; } f(true);
你可能從前面的部分想起我們已經(jīng)在 V8 中提供了函數(shù)調(diào)用次數(shù)和源碼范圍。不幸的是,這不適合塊覆蓋的場景,我們必須實現(xiàn)新的機制來收集執(zhí)行次數(shù)和它們相應(yīng)的源碼范圍。
第一個方面是源碼范圍:假設(shè)我們擁有一個特定塊的執(zhí)行計數(shù),我們?nèi)绾螌⑺鼈冇成涞皆创a的一部分呢? 為此,我們需要在解析源文件時收集相關(guān)位置信息。在塊覆蓋之前,V8已經(jīng)在某種程度上做到了這一點。一個示例是由如上所述的Function.prototype.toString而觸發(fā)的函數(shù)范圍的收集。
另一個例子是用于構(gòu)造Error對象的回溯的源碼位置。但這些都不足以支持塊覆蓋; 前者僅適用于函數(shù),而后者僅保存位置信息(例如if-else語句的if標(biāo)記的位置),而不是源碼范圍。
因此,我們必須擴展解析器以收集源碼范圍。為了演示,假設(shè)我們正在使用if-else語句:
if (cond) { /* Then branch. */ } else { /* Else branch. */ }
當(dāng)啟用塊覆蓋時,我們收集 then 和 else 分支的源碼范圍,并將它們與已解析的 IfStatement AST 節(jié)點相關(guān)聯(lián)。其他相關(guān)語言結(jié)構(gòu)也是如此處理。
在解析過程中收集完源碼范圍集之后,第二個方面是在運行時跟蹤執(zhí)行計數(shù)。 這是通過在生成的字節(jié)碼數(shù)組的關(guān)鍵位置插入新的專用 IncBlockCounter 字節(jié)碼來完成的。在運行時,IncBlockCounter 字節(jié)碼處理程序只是增加對應(yīng)的計數(shù)器接口(可通過函數(shù)對象訪問)。
在 if-else 語句的上述示例中,這樣的字節(jié)碼將被插入在三個位置:緊接在 then 分支的主體之前,在 else 分支的主體之前,緊接在 if-else 語句之后(由于分支內(nèi)可能存在非本地控制,因此需要連續(xù)的計數(shù)器)。
最后,報告塊粒度覆蓋與函數(shù)粒度報告類似。但除了調(diào)用計數(shù)(來自反饋向量)之外,我們現(xiàn)在還報告了感興趣的源范圍的集合以及它們的塊計數(shù)(存儲在掛起該函數(shù)的輔助數(shù)據(jù)結(jié)構(gòu)中)。
如果您想了解V8中代碼覆蓋之后相關(guān)技術(shù)細(xì)節(jié)的更多信息,請參閱coverage和block coverage設(shè)計文檔。
感謝各位的閱讀!關(guān)于“JavaScript代碼覆蓋的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
當(dāng)前題目:JavaScript代碼覆蓋的示例分析
網(wǎng)頁鏈接:http://redsoil1982.com.cn/article40/jdghho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、網(wǎng)頁設(shè)計公司、軟件開發(fā)、App設(shè)計、品牌網(wǎng)站設(shè)計、全網(wǎng)營銷推廣
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)