基於文本類似度算法,分析 Vue 是抄出來的框架嗎?

本週一篇指摘 Vue 抄襲 Angular 的文章一石激起千層浪。爲此,筆者做爲中立吃瓜的 React 用戶,分析了 13 個主流前端框架版本上萬個變量的命名風格,應用天然語言處理中的文本類似度算法進行了分析,以對這一論點的有效性作出客觀的評價。html

思路

在分析書籍抄襲、論文查重等場景下,使用算法比較文本類似度的方法是一種有效的技術手段。那麼,咱們如何經過這一手段,來分析源碼層面的抄襲呢?前端

在對比形如 我喜歡寫代碼,不喜歡撕逼我不喜歡撕逼,喜歡寫代碼 的兩個句子類似度時,大體的思路是首先分詞,然後計算出詞頻,再將詞頻向量化,最後比較兩個高維向量的夾角,夾角越小則越類似。vue

在【Vue 是否抄襲了 Angular】這一場景下,咱們分析的對象從句子變成了代碼。這時主要的區別是這兩點:node

  1. 代碼是高度結構化的文本,分詞已經經過詞法分析器完成了。
  2. 某種編程語言的代碼中,充斥着大量該語言的關鍵字。如 varfunction 的關鍵字,這些關鍵字的重複與類似度無關。

而對因而否抄襲與類似度的關係,咱們給出這幾個假設:react

  1. 解決一樣的開放性問題時,獨立編寫、不存在抄襲的代碼,以變量名爲表明的編碼風格一般有巨大的差異,此時類似度是低的
  2. 存在較大規模抄襲的代碼,相似於同一個框架,未經大規模重構的不一樣版本代碼。此時編碼風格是相似的,此時類似度是高的
  3. 先後調換模塊聲明順序,不影響類似度

給定這幾個前提後,咱們能夠肯定出這樣的分析策略:jquery

  1. 輸入各大框架未經壓縮的源碼,解析出其語法樹。
  2. 捨棄語法樹中無關部分,提取出其變量聲明以表明其編碼風格。
  3. 使用文本類似度算法計算變量名間的類似度,分析結論。

變量名提取

在經過 Webpack 引用框架依賴時,一般導入的都是打包成單一文件且未經混淆的框架源碼。這是一個很是好的特性。筆者編寫了一個簡單的 Webpack Loader 以在這個過程當中實現變量名的提取:git

// loader/index.js
// 爲 loader 傳入的 content 即爲 JS 源碼
module.exports = function (content) {
  return demo(content)
}複製代碼

demo 函數中得到框架源碼後,解析語法樹也不是一個困難的問題了。經過 acorn 這一 Parser 咱們就能作到:github

function demo (content) {
  const ast = acorn.parse(content, { sourceType: 'module' })
  walk.simple(ast, {
    // 在 walk 遍歷時,抽取所有變量聲明語句中的變量名
    VariableDeclaration (node) {
      const name = node.declarations[0].id.name + '\n'
      fs.appendFileSync(resolve('./result.txt'), name)
    }
  })
  return content
}複製代碼

這時候咱們就能在 result.txt 內得到一個前端框架中的所有變量名了,形如:面試

p
i
resolved
c
segs
i
...複製代碼

這都是什麼亂七八糟的…這時候咱們得到的文本並無通過初步的處理,咱們真正感興趣的是各個框架變量名的詞頻。詞頻的計算是一道不錯的面試題,不過在這裏咱們直接經過 Wordclouds 的服務來實現這一步。這一步中還包括基本的清洗,以去除 i / a / b 這些無心義的變量名。咱們的結果是形如這樣的格式:算法

29    value
19    arg
18    result
16    key
14    index
...複製代碼

以上就是 React / Vue / Angular 三大框架中某一個的 Top 5 變量名,猜猜是哪個?好吧這幾個變量名都十分爛大街…暫時看不出什麼端倪。讓咱們繼續作類似度比較吧,答案在後面揭曉。

類似度算法

咱們在上文中,實際上已經得到了這樣的對象:

const a = {
  'foo': 5,
  'bar': 4,
  'baz': 3
}
const b = {
  'foo': 4,
  'bar': 6,
  'baz': 0
}複製代碼

咱們能夠認爲,每一個變量名是一個獨立的維度,每一個框架中存在的全部類型變量名組成一個高維空間的向量。從而,咱們的問題就簡化爲了如何比較 a 與 b 這兩個向量的類似程度。在此引用阮一峯老師的介紹

咱們能夠把它們想象成空間中的兩條線段,都是從原點([0, 0, ...])出發,指向不一樣的方向。兩條線段之間造成一個夾角,若是夾角爲0度,意味着方向相同、線段重合;若是夾角爲90度,意味着造成直角,方向徹底不類似;若是夾角爲180度,意味着方向正好相反。所以,咱們能夠經過夾角的大小,來判斷向量的類似程度。夾角越小,就表明越類似。

theta-1
theta-1

假定 a 向量是 [x1, y1],b向量是 [x2, y2],那麼能夠將餘弦定理改寫成下面的形式,計算出的 cosθ 表明類似度:

theta-2
theta-2

推廣到高維向量的通常情形:

theta-3
theta-3

根據算法編寫出簡化的示例代碼:

function getTheta () {
  let x = 0
  Object.keys(dictAll).forEach(key => {
    if (dictA[key] && dictB[key]) x += dictA[key] * dictB[key]
  })
  let yA = getY(dictA)
  let yB = getY(dictB)
  const result = x / (yA * yB)
  console.log(result)
}複製代碼

最後運行咱們的分析算法處理上一步的變量名便可:

➜ node analyse vue@2.4.1 vue@2.4.2
0.9436438155995188複製代碼

實驗結果與總結

一系列鋪墊之後,終於到了檢驗真理的時候了。咱們首先基於【類似版本類似度高】的假設,驗證 Vue 是否符合這一假設:

➜ node analyse vue@2.4.1 vue@2.4.2
0.9436438155995188複製代碼

能夠看到,目前最新的 vue 2.4.2 與 2.4.1 之間,確實存在着很高的類似度。接下來比較 vue 最新版與 2.0.0 同一個 Major 版本之間的類似度:

➜ node analyse vue@2.0.0 vue@2.4.2
0.8838059164881868複製代碼

類似度有所下降,說明最新版比起去年的 V2,已經有了不小的改動了。再來比較 V2 與 V1 系列的類似度:

➜ node analyse vue@2.0.0 vue@1.0.28
0.5883193867742227複製代碼

類似度明顯下降,顯然重構之言非虛。最後比較 Vue 的最新版與第一個版本:

➜ node analyse vue@2.4.2 vue@0.6.0
0.4590386014371645複製代碼

這是 Vue 家族中最低的類似度,也達到了 0.45 的水平。接下來是正戲,比較 Vue 和 Angular 的最新版:

➜ node analyse vue@2.4.2 angular@4.3.3
0.19322280449484375複製代碼

區區 0.19 的類似度!好吧,Angular 最新版也是重構過的,咱們不妨直接比較最先【照着 Angular 抄的】的 Vue 和 Angular 1.x 系列:

➜ node analyse vue@0.6.0 angular@1.2.32
0.294527560626686複製代碼

這個類似度也大幅低於 Vue 全系列縱向對比的類似度!爲了更有效地對比,咱們讓隔壁 React 躺個槍(未加版本號表明最新版):

➜ node analyse vue@2.4.2 react 
0.27592736925848194複製代碼

0.27 與 0.29 的對比,說明即使是最先階段(與 Angular 類似度最高)的 Vue,類似度也僅僅至關於如今的 Vue 和 React 而已!爲了保證公平,咱們讓 jQuery 也來湊個熱鬧:

➜ node analyse jquery angular@1.2.32
0.2508302720623658複製代碼

這也是不到 0.3 的類似度,據此咱們甚至能夠得出一個大膽的結論:Vue 和 Angular 的類似度,和 Angular 與 jQuery 之間的類似度接近!沒有人會認爲 jQuery 與 Angular 之間存在抄襲吧?

固然,Vue 和 Angular 的類似度是客觀存在的。咱們在前端領域,能夠找到另外一對這樣的例子:jQuery VS Zepto,它們之間的類似度如何呢?

➜ node analyse jquery zepto
0.25994377334635854複製代碼

這個類似度和 Angular VS jQuery 幾乎相同,這說明即使設計理念相近,具體實現不一樣的原創框架之間,類似度也是很低的。Vue VS Angular 也徹底符合這一結論。

hmmm 目前咱們的論據已經比較充分了。最後,咱們比較一種情形:設計理念徹底不一樣的原創框架之間,類似度如何?咱們拉出 jQuery 和 React:

➜ node analyse jquery react        
0.1007248324385447複製代碼

全場最低類似度…因此咱們能夠理解 jQuery 時代的前端轉向 React 時有多麼不習慣了吧😂

到此爲止,咱們的結論有:

  • Vue 系列迭代間類似度較高。
  • 即使是最先的 Vue,與經典 Angular 的類似度也很低。
  • 最新 Vue 與最新 Angular 之間,類似度更低,說明兩者的發展道路早已更加獨立。
  • 即使設計理念相近,具體實現不一樣的原創框架之間,類似度也很低。
  • React 與 jQuery 的類似度特別特別低(離題了)。

據此,筆者有理由認爲【Vue 抄襲了 Angular】的論點是站不住腳的。

本文的實驗數據託管在 Github 上,歡迎感興趣的同窗驗證並改進這些結論。最後,框架畢竟只是工具,相互撕逼併不利於社區的發展。引用我司 Boss 的觀點:【一流的人作事,二流的人去評論,三流的人去評論別人的評論】,但願你們能把口水戰的時間放在更務實的事情上,推進技術水平、社區氛圍和平均工資的上升……

相關文章
相關標籤/搜索