自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

使用 jsinspect 檢測(cè)前端代碼庫(kù)中的重復(fù)/近似代碼

開(kāi)發(fā) 開(kāi)發(fā)工具
在開(kāi)發(fā)的過(guò)程中我們往往會(huì)存在大量的復(fù)制粘貼代碼的行為,這一點(diǎn)在項(xiàng)目的開(kāi)發(fā)初期尤其顯著;而在項(xiàng)目逐步穩(wěn)定,功能需求逐步完善之后我們就需要考慮對(duì)代碼庫(kù)的優(yōu)化與重構(gòu),盡量編寫清晰可維護(hù)的代碼。

[[187087]]

在開(kāi)發(fā)的過(guò)程中我們往往會(huì)存在大量的復(fù)制粘貼代碼的行為,這一點(diǎn)在項(xiàng)目的開(kāi)發(fā)初期尤其顯著;而在項(xiàng)目逐步穩(wěn)定,功能需求逐步完善之后我們就需要考慮對(duì)代碼庫(kù)的優(yōu)化與重構(gòu),盡量編寫清晰可維護(hù)的代碼。好的代碼往往是在合理范圍內(nèi)盡可能地避免重復(fù)代碼,遵循單一職責(zé)與 Single Source of Truth 等原則,本部分我們嘗試使用 jsinspect 對(duì)于代碼庫(kù)進(jìn)行自動(dòng)檢索,根據(jù)其反饋的重復(fù)或者近似的代碼片進(jìn)行合理的優(yōu)化。當(dāng)然,我們并不是單純地追求公共代碼地完全剝離化,過(guò)度的抽象反而會(huì)降低代碼的可讀性與可理解性。jsinspect 利用 babylon 對(duì)于 JavaScript 或者 JSX 代碼構(gòu)建 AST 語(yǔ)法樹(shù),根據(jù)不同的 AST 節(jié)點(diǎn)類型,譬如 BlockStatement、VariableDeclaration、ObjectExpression 等標(biāo)記相似結(jié)構(gòu)的代碼塊。我們可以使用 npm 全局安裝 jsinspect 命令:

  1. Usage: jsinspect [options] <paths ...> 
  2.  
  3.  
  4. Detect copy-pasted and structurally similar JavaScript code 
  5. Example use: jsinspect -I -L -t 20 --ignore "test" ./path/to/src 
  6.  
  7.  
  8. Options: 
  9.  
  10.   -h, --help                         output usage information 
  11.   -V, --version                      output the version number 
  12.   -t, --threshold <number>           number of nodes (default: 30) 
  13.   -m, --min-instances <number>       min instances for a match (default: 2) 
  14.   -c, --config                       path to config file (default: .jsinspectrc) 
  15.   -r, --reporter [default|json|pmd]  specify the reporter to use 
  16.   -I, --no-identifiers               do not match identifiers 
  17.   -L, --no-literals                  do not match literals 
  18.   -C, --no-color                     disable colors 
  19.   --ignore <pattern>                 ignore paths matching a regex 
  20.   --truncate <number>                length to truncate lines (default: 100, off: 0) 

我們也可以選擇在項(xiàng)目目錄下添加 .jsinspect 配置文件指明 jsinspect 運(yùn)行配置:

  1.   "threshold":     30, 
  2.   "identifiers":   true
  3.   "literals":      true
  4.   "ignore":        "test|spec|mock"
  5.   "reporter":      "json"
  6.   "truncate":      100, 

在配置完畢之后,我們可以使用 jsinspect -t 50 --ignore "test" ./path/to/src 來(lái)對(duì)于代碼庫(kù)進(jìn)行分析,以筆者找到的某個(gè)代碼庫(kù)為例,其檢測(cè)出了上百個(gè)重復(fù)的代碼片,其中典型的代表如下所示??梢钥吹皆谀硞€(gè)組件中重復(fù)編寫了多次密碼輸入的元素,我們可以選擇將其封裝為函數(shù)式組件,將 label、hintText 等通用屬性包裹在內(nèi),從而減少代碼的重復(fù)率。

  1. Match - 2 instances 
  2.  
  3. ./src/view/main/component/tabs/account/operation/login/forget_password.js:96,110 
  4. return <div className="my_register__register"
  5.     <div className="item"
  6.         <Paper zDepth={2}> 
  7.             <EnhancedTextFieldWithLabel 
  8.                 label="密碼" 
  9.                 hintText="請(qǐng)輸入密碼,6-20位字母,數(shù)字" 
  10.                 onChange={(event, value)=> { 
  11.                     this.setState({ 
  12.                         userPwd: value 
  13.                     }) 
  14.                 }} 
  15.             /> 
  16.         </Paper> 
  17.     </div> 
  18.     <div className="item"
  19.  
  20. ./src/view/main/component/tabs/my/login/forget_password.js:111,125 
  21. return <div className="my_register__register"
  22.     <div className="item"
  23.         <Paper zDepth={2}> 
  24.             <EnhancedTextFieldWithLabel 
  25.                 label="密碼" 
  26.                 hintText="請(qǐng)輸入密碼,6-20位字母,數(shù)字" 
  27.                 onChange={(event, value)=> { 
  28.                     this.setState({ 
  29.                         userPwd: value 
  30.                     }) 
  31.                 }} 
  32.             /> 
  33.         </Paper> 
  34.     </div> 
  35.     <div className="item"

筆者也對(duì)于 React 源碼進(jìn)行了簡(jiǎn)要分析,在 246 個(gè)文件中共發(fā)現(xiàn) 16 個(gè)近似代碼片,并且其中的大部分重復(fù)源于目前基于 Stack 的調(diào)和算法與基于 Fiber 重構(gòu)的調(diào)和算法之間的過(guò)渡時(shí)期帶來(lái)的重復(fù),譬如:

  1. Match - 2 instances 
  2.  
  3. ./src/renderers/dom/fiber/wrappers/ReactDOMFiberTextarea.js:134,153 
  4.   var value = props.value; 
  5.   if (value != null) { 
  6.     // Cast `value` to a string to ensure the value is set correctly. While 
  7.     // browsers typically do this as necessary, jsdom doesn't. 
  8.     var newValue = '' + value; 
  9.  
  10.     // To avoid side effects (such as losing text selection), only set value if changed 
  11.     if (newValue !== node.value) { 
  12.       node.value = newValue; 
  13.     } 
  14.     if (props.defaultValue == null) { 
  15.       node.defaultValue = newValue; 
  16.     } 
  17.   } 
  18.   if (props.defaultValue != null) { 
  19.     node.defaultValue = props.defaultValue; 
  20.   } 
  21. }, 
  22.  
  23. postMountWrapper: function(element: Element, props: Object) { 
  24.  
  25. ./src/renderers/dom/stack/client/wrappers/ReactDOMTextarea.js:129,148 
  26.   var value = props.value; 
  27.   if (value != null) { 
  28.     // Cast `value` to a string to ensure the value is set correctly. While 
  29.     // browsers typically do this as necessary, jsdom doesn't. 
  30.     var newValue = '' + value; 
  31.  
  32.     // To avoid side effects (such as losing text selection), only set value if changed 
  33.     if (newValue !== node.value) { 
  34.       node.value = newValue; 
  35.     } 
  36.     if (props.defaultValue == null) { 
  37.       node.defaultValue = newValue; 
  38.     } 
  39.   } 
  40.   if (props.defaultValue != null) { 
  41.     node.defaultValue = props.defaultValue; 
  42.   } 
  43. }, 
  44.  
  45. postMountWrapper: function(inst) { 

筆者認(rèn)為在新特性的開(kāi)發(fā)過(guò)程中我們不一定需要時(shí)刻地考慮代碼重構(gòu),而是應(yīng)該相對(duì)獨(dú)立地開(kāi)發(fā)新功能。***我們?cè)俸?jiǎn)單地討論下 jsinspect 的工作原理,這樣我們可以在項(xiàng)目需要時(shí)自定義類似的工具以進(jìn)行特殊代碼的匹配或者提取。jsinspect 的核心工作流可以反映在 inspector.js 文件中:

  1. ...  
  2. this._filePaths.forEach((filePath) => { 
  3.   var src = fs.readFileSync(filePath, {encoding: 'utf8'}); 
  4.   this._fileContents[filePath] = src.split('\n'); 
  5.   var syntaxTree = parse(src, filePath); 
  6.   this._traversals[filePath] = nodeUtils.getDFSTraversal(syntaxTree); 
  7.   this._walk(syntaxTree, (nodes) => this._insert(nodes)); 
  8. }); 
  9.  
  10. this._analyze(); 
  11. ... 

上述流程還是較為清晰的,jsinspect 會(huì)遍歷所有的有效源碼文件,提取其源碼內(nèi)容然后通過(guò) babylon 轉(zhuǎn)化為 AST 語(yǔ)法樹(shù),某個(gè)文件的語(yǔ)法樹(shù)格式如下:

  1. Node { 
  2.   type: 'Program'
  3.   start: 0, 
  4.   end: 31, 
  5.   loc: 
  6.    SourceLocation { 
  7.      start: Position { line: 1, column: 0 }, 
  8.      end: Position { line: 2, column: 15 }, 
  9.      filename: './__test__/a.js' }, 
  10.   sourceType: 'script'
  11.   body: 
  12.    [ Node { 
  13.        type: 'ExpressionStatement'
  14.        start: 0, 
  15.        end: 15, 
  16.        loc: [Object], 
  17.        expression: [Object] }, 
  18.      Node { 
  19.        type: 'ExpressionStatement'
  20.        start: 16, 
  21.        end: 31, 
  22.        loc: [Object], 
  23.        expression: [Object] } ], 
  24.   directives: [] } 
  25. './__test__/a.js': [ 'console.log(a);''console.log(b);' ] } 

其后我們通過(guò)深度優(yōu)先遍歷算法在 AST 語(yǔ)法樹(shù)上構(gòu)建所有節(jié)點(diǎn)的數(shù)組,然后遍歷整個(gè)數(shù)組構(gòu)建待比較對(duì)象。這里我們?cè)谶\(yùn)行時(shí)輸入的 -t 參數(shù)就是用來(lái)指定分割的原子比較對(duì)象的維度,當(dāng)我們將該參數(shù)指定為 2 時(shí),經(jīng)過(guò)遍歷構(gòu)建階段形成的內(nèi)部映射數(shù)組 _map 結(jié)構(gòu)如下:

  1. 'uj3VAExwF***vx0SGBDFu8beU+Lk=': [ [ [Object], [Object] ], [ [Object], [Object] ] ], 
  2.   'eMqg1hUXEFYNbKkbsd2QWECLiYU=': [ [ [Object], [Object] ], [ [Object], [Object] ] ], 
  3.   'gvSCaZfmhte6tfnpfmnTeH+eylw=': [ [ [Object], [Object] ], [ [Object], [Object] ] ], 
  4.   'eHqT9EuPomhWLlo9nwU0DWOkcXk=': [ [ [Object], [Object] ], [ [Object], [Object] ] ] } 

如果有大規(guī)模代碼數(shù)據(jù)的話我們可能形成很多有重疊的實(shí)例,這里使用了 _omitOverlappingInstances 函數(shù)來(lái)進(jìn)行去重;譬如如果某個(gè)實(shí)例包含節(jié)點(diǎn) abcd,另一個(gè)實(shí)例包含節(jié)點(diǎn)組 bcde,那么會(huì)選擇將后者從數(shù)組中移除。另一個(gè)優(yōu)化加速的方法就是在每次比較結(jié)束之后移除已經(jīng)匹配到的代碼片:

  1. _prune(nodeArrays) { 
  2.   for (let i = 0; i < nodeArrays.length; i++) { 
  3.     let nodes = nodeArrays[i]; 
  4.     for (let j = 0; j < nodes.length; j++) { 
  5.       this._removeNode(nodes[j]); 
  6.     } 
  7.   } 
  8. }

【本文是51CTO專欄作者“張梓雄 ”的原創(chuàng)文章,如需轉(zhuǎn)載請(qǐng)通過(guò)51CTO與作者聯(lián)系】

戳這里,看該作者更多好文

責(zé)任編輯:武曉燕 來(lái)源: 51CTO專欄
相關(guān)推薦

2009-07-22 07:45:00

Scala代碼重復(fù)

2009-07-15 18:07:47

JDBC代碼

2022-09-23 14:44:31

前端huskyeslint

2022-04-07 10:02:58

前端檢測(cè)工具

2021-08-08 08:08:20

木馬無(wú)文件Cobalt Stri

2022-08-01 23:45:23

代碼識(shí)別項(xiàng)目

2014-01-08 09:33:57

重復(fù)IP地址IP檢測(cè)

2021-04-22 15:08:01

代碼評(píng)審郵件

2021-09-03 08:21:20

前端代碼模塊

2023-12-04 07:06:11

2023-09-26 08:29:27

2025-02-17 07:00:00

ORB對(duì)象跟蹤器計(jì)算機(jī)視覺(jué)

2016-10-19 20:34:46

2022-08-28 10:08:53

前端代碼前端

2023-11-23 13:07:18

代碼Golang

2011-08-24 12:49:56

SQL Server托管代碼

2011-05-05 09:54:05

靜態(tài)代碼

2024-06-21 09:19:45

代碼接口重復(fù)請(qǐng)求開(kāi)發(fā)

2023-05-31 08:00:00

PromptrGPT人工智能

2013-03-06 09:41:29

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)