摘要:使用檢測前端代碼庫中的重復(fù)近似代碼從屬于筆者的前端入門與工程實(shí)踐,更多前端相關(guān)學(xué)習(xí)資料推薦閱讀前端每周清單第期學(xué)習(xí)資源,發(fā)布,六問程序員如何成長泛前端知識圖譜。利用對于或者代碼構(gòu)建語法樹,根據(jù)不同的節(jié)點(diǎn)類型,譬如等標(biāo)記相似結(jié)構(gòu)的代碼塊。
使用 jsinspect 檢測前端代碼庫中的重復(fù)/近似代碼 從屬于筆者的 Web 前端入門與工程實(shí)踐,更多前端相關(guān)學(xué)習(xí)資料推薦閱讀前端每周清單第6期:Angular 4.0學(xué)習(xí)資源,Egg.js 1.0發(fā)布,六問CTO程序員如何成長、泛前端知識圖譜(Web/iOS/Android/RN)。
在開發(fā)的過程中我們往往會存在大量的復(fù)制粘貼代碼的行為,這一點(diǎn)在項(xiàng)目的開發(fā)初期尤其顯著;而在項(xiàng)目逐步穩(wěn)定,功能需求逐步完善之后我們就需要考慮對代碼庫的優(yōu)化與重構(gòu),盡量編寫清晰可維護(hù)的代碼。好的代碼往往是在合理范圍內(nèi)盡可能地避免重復(fù)代碼,遵循單一職責(zé)與 Single Source of Truth 等原則,本部分我們嘗試使用 jsinspect 對于代碼庫進(jìn)行自動檢索,根據(jù)其反饋的重復(fù)或者近似的代碼片進(jìn)行合理的優(yōu)化。當(dāng)然,我們并不是單純地追求公共代碼地完全剝離化,過度的抽象反而會降低代碼的可讀性與可理解性。jsinspect 利用 babylon 對于 JavaScript 或者 JSX 代碼構(gòu)建 AST 語法樹,根據(jù)不同的 AST 節(jié)點(diǎn)類型,譬如 BlockStatement、VariableDeclaration、ObjectExpression 等標(biāo)記相似結(jié)構(gòu)的代碼塊。我們可以使用 npm 全局安裝 jsinspect 命令:
Usage: jsinspect [options]Detect copy-pasted and structurally similar JavaScript code Example use: jsinspect -I -L -t 20 --ignore "test" ./path/to/src Options: -h, --help output usage information -V, --version output the version number -t, --threshold number of nodes (default: 30) -m, --min-instances min instances for a match (default: 2) -c, --config path to config file (default: .jsinspectrc) -r, --reporter [default|json|pmd] specify the reporter to use -I, --no-identifiers do not match identifiers -L, --no-literals do not match literals -C, --no-color disable colors --ignore ignore paths matching a regex --truncate length to truncate lines (default: 100, off: 0)
我們也可以選擇在項(xiàng)目目錄下添加 .jsinspect 配置文件指明 jsinspect 運(yùn)行配置:
{ "threshold": 30, "identifiers": true, "literals": true, "ignore": "test|spec|mock", "reporter": "json", "truncate": 100, }
在配置完畢之后,我們可以使用 jsinspect -t 50 --ignore "test" ./path/to/src 來對于代碼庫進(jìn)行分析,以筆者找到的某個(gè)代碼庫為例,其檢測出了上百個(gè)重復(fù)的代碼片,其中典型的代表如下所示。可以看到在某個(gè)組件中重復(fù)編寫了多次密碼輸入的元素,我們可以選擇將其封裝為函數(shù)式組件,將 label、hintText 等通用屬性包裹在內(nèi),從而減少代碼的重復(fù)率。
Match - 2 instances ./src/view/main/component/tabs/account/operation/login/forget_password.js:96,110 return{ this.setState({ userPwd: value }) }} /> ./src/view/main/component/tabs/my/login/forget_password.js:111,125 return{ this.setState({ userPwd: value }) }} /> 筆者也對于 React 源碼進(jìn)行了簡要分析,在 246 個(gè)文件中共發(fā)現(xiàn) 16 個(gè)近似代碼片,并且其中的大部分重復(fù)源于目前基于 Stack 的調(diào)和算法與基于 Fiber 重構(gòu)的調(diào)和算法之間的過渡時(shí)期帶來的重復(fù),譬如:
Match - 2 instances ./src/renderers/dom/fiber/wrappers/ReactDOMFiberTextarea.js:134,153 var value = props.value; if (value != null) { // Cast `value` to a string to ensure the value is set correctly. While // browsers typically do this as necessary, jsdom doesn"t. var newValue = "" + value; // To avoid side effects (such as losing text selection), only set value if changed if (newValue !== node.value) { node.value = newValue; } if (props.defaultValue == null) { node.defaultValue = newValue; } } if (props.defaultValue != null) { node.defaultValue = props.defaultValue; } }, postMountWrapper: function(element: Element, props: Object) { ./src/renderers/dom/stack/client/wrappers/ReactDOMTextarea.js:129,148 var value = props.value; if (value != null) { // Cast `value` to a string to ensure the value is set correctly. While // browsers typically do this as necessary, jsdom doesn"t. var newValue = "" + value; // To avoid side effects (such as losing text selection), only set value if changed if (newValue !== node.value) { node.value = newValue; } if (props.defaultValue == null) { node.defaultValue = newValue; } } if (props.defaultValue != null) { node.defaultValue = props.defaultValue; } }, postMountWrapper: function(inst) {筆者認(rèn)為在新特性的開發(fā)過程中我們不一定需要時(shí)刻地考慮代碼重構(gòu),而是應(yīng)該相對獨(dú)立地開發(fā)新功能。最后我們再簡單地討論下 jsinspect 的工作原理,這樣我們可以在項(xiàng)目需要時(shí)自定義類似的工具以進(jìn)行特殊代碼的匹配或者提取。jsinspect 的核心工作流可以反映在 inspector.js 文件中:
... this._filePaths.forEach((filePath) => { var src = fs.readFileSync(filePath, {encoding: "utf8"}); this._fileContents[filePath] = src.split(" "); var syntaxTree = parse(src, filePath); this._traversals[filePath] = nodeUtils.getDFSTraversal(syntaxTree); this._walk(syntaxTree, (nodes) => this._insert(nodes)); }); this._analyze(); ...上述流程還是較為清晰的,jsinspect 會遍歷所有的有效源碼文件,提取其源碼內(nèi)容然后通過 babylon 轉(zhuǎn)化為 AST 語法樹,某個(gè)文件的語法樹格式如下:
Node { type: "Program", start: 0, end: 31, loc: SourceLocation { start: Position { line: 1, column: 0 }, end: Position { line: 2, column: 15 }, filename: "./__test__/a.js" }, sourceType: "script", body: [ Node { type: "ExpressionStatement", start: 0, end: 15, loc: [Object], expression: [Object] }, Node { type: "ExpressionStatement", start: 16, end: 31, loc: [Object], expression: [Object] } ], directives: [] } { "./__test__/a.js": [ "console.log(a);", "console.log(b);" ] }其后我們通過深度優(yōu)先遍歷算法在 AST 語法樹上構(gòu)建所有節(jié)點(diǎn)的數(shù)組,然后遍歷整個(gè)數(shù)組構(gòu)建待比較對象。這里我們在運(yùn)行時(shí)輸入的 -t 參數(shù)就是用來指定分割的原子比較對象的維度,當(dāng)我們將該參數(shù)指定為 2 時(shí),經(jīng)過遍歷構(gòu)建階段形成的內(nèi)部映射數(shù)組 _map 結(jié)構(gòu)如下:
{ "uj3VAExwF5Avx0SGBDFu8beU+Lk=": [ [ [Object], [Object] ], [ [Object], [Object] ] ], "eMqg1hUXEFYNbKkbsd2QWECLiYU=": [ [ [Object], [Object] ], [ [Object], [Object] ] ], "gvSCaZfmhte6tfnpfmnTeH+eylw=": [ [ [Object], [Object] ], [ [Object], [Object] ] ], "eHqT9EuPomhWLlo9nwU0DWOkcXk=": [ [ [Object], [Object] ], [ [Object], [Object] ] ] }如果有大規(guī)模代碼數(shù)據(jù)的話我們可能形成很多有重疊的實(shí)例,這里使用了 _omitOverlappingInstances 函數(shù)來進(jìn)行去重;譬如如果某個(gè)實(shí)例包含節(jié)點(diǎn) abcd,另一個(gè)實(shí)例包含節(jié)點(diǎn)組 bcde,那么會選擇將后者從數(shù)組中移除。另一個(gè)優(yōu)化加速的方法就是在每次比較結(jié)束之后移除已經(jīng)匹配到的代碼片:
_prune(nodeArrays) { for (let i = 0; i < nodeArrays.length; i++) { let nodes = nodeArrays[i]; for (let j = 0; j < nodes.length; j++) { this._removeNode(nodes[j]); } } }文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/82229.html
相關(guān)文章
JavaScript權(quán)威指南筆記(上)-語言核心
摘要:二進(jìn)制浮點(diǎn)數(shù)和四舍五入錯(cuò)誤在使用實(shí)數(shù)時(shí),常常只是真實(shí)值的一個(gè)近似表示。作用域分類全局函數(shù),塊級作用域鏈變量對象用于存儲執(zhí)行上下文中的變量函數(shù)聲明函數(shù)參數(shù)變量初始化階段瀏覽器截圖瀏覽器截圖代碼執(zhí)行階段類和模塊 詞法結(jié)構(gòu) 字符集 使用Unicode編寫 ES3 Unicode2.1+ ES5 Unicode3+ 區(qū)分大小寫 注釋 // 注釋 /* 注釋 */ 標(biāo)識符和保留字 必須以字...
咋做長文本去重
摘要:新問題拋出有沒有一種簽名算法,如果文本非常相似,簽名值也非常相似呢二文本相似性的簽名算法上文提出的問題,可以用局部敏感哈希解決,局部敏感哈希是一類文本越相似,哈希值越相似的算法,有興趣的同學(xué)自行百度,這里分享一下的思路。 緣起:(1)原創(chuàng)不易,互聯(lián)網(wǎng)抄襲成風(fēng),很多原創(chuàng)內(nèi)容在網(wǎng)上被抄來抄去,改來改去(2)百度的網(wǎng)頁庫非常大,爬蟲如何判斷一個(gè)新網(wǎng)頁是否與網(wǎng)頁庫中已有的網(wǎng)頁重復(fù)呢?這是本文要...
算法分析 - Algorithms, Part I, week 1 ANALYSIS OF ALGO
摘要:實(shí)際上這個(gè)情形中存在冪定律實(shí)際上絕大多數(shù)的計(jì)算機(jī)算法的運(yùn)行時(shí)間滿足冪定律。基于研究得知,原則上我們能夠獲得算法,程序或者操作的性能的精確數(shù)學(xué)模型。 前言 上一篇:并查集下一篇:棧和隊(duì)列 在算法性能上我們常常面臨的挑戰(zhàn)是我們的程序能否求解實(shí)際中的大型輸入:--為什么程序運(yùn)行的慢?--為什么程序耗盡了內(nèi)存? 沒有理解算法的性能特征會導(dǎo)致客戶端的性能很差,為了避免這種情況的出線,需要具備算法...
JavaScript 工作原理之三-內(nèi)存管理及如何處理 4 類常見的內(nèi)存泄漏問題(譯)
摘要:這是因?yàn)槲覀冊L問了數(shù)組中不存在的數(shù)組元素它超過了最后一個(gè)實(shí)際分配到內(nèi)存的數(shù)組元素字節(jié),并且有可能會讀取或者覆寫的位。包含個(gè)元素的新數(shù)組由和數(shù)組元素所組成中的內(nèi)存使用中使用分配的內(nèi)存主要指的是內(nèi)存讀寫。 原文請查閱這里,本文有進(jìn)行刪減,文后增了些經(jīng)驗(yàn)總結(jié)。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第三章。 我們將會討論日常使用中另一個(gè)被開發(fā)...
發(fā)表評論
0條評論
ZoomQuiet
男|高級講師
TA的文章
閱讀更多
#黑五#DediPath:VPS和混合服務(wù)器永久3.5折,獨(dú)立服務(wù)器$39/月起
閱讀 1311·2021-11-24 10:24
基于Vue3最新標(biāo)準(zhǔn),實(shí)現(xiàn)后臺前端綜合解決方案MK
閱讀 4089·2021-11-22 15:29
Issues with position fixed & scroll(移動端 fixed
閱讀 1085·2019-08-30 15:53
原生CSS+JS實(shí)現(xiàn)連續(xù)點(diǎn)擊旋轉(zhuǎn)
閱讀 2788·2019-08-30 10:54
一小波DOM騷操作:querySelectorAll和classList
閱讀 1977·2019-08-29 17:26
步步向前之Element-UI
閱讀 1271·2019-08-29 17:08
詳解 CSS 屬性 - 優(yōu)先級問題
閱讀 605·2019-08-28 17:55
Jenkins與Rainbond對接部署應(yīng)用
閱讀 1576·2019-08-26 14:01