日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
前端大數(shù)的運算及相關知識總結

 背景

前段時間我在公司的項目中負責的是權限管理這一塊的需求。需求的大概內容就是系統(tǒng)的管理員可以在用戶管理界面對用戶和用戶扮演的角色進行增刪改查的操作,然后當用戶進入主應用時,前端會請求到一個表示用戶權限的數(shù)組usr_permission,前端通過usr_permission來判斷用戶是否擁有某項權限。

創(chuàng)新互聯(lián)是一家專業(yè)提供和平企業(yè)網(wǎng)站建設,專注與網(wǎng)站設計制作、做網(wǎng)站、H5建站、小程序制作等業(yè)務。10年已為和平眾多企業(yè)、政府機構等服務。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站設計公司優(yōu)惠進行中。

這個usr_permission是一個長度為16的大數(shù)字符串數(shù)組,如下所示:

 
 
 
  1. const usr_permission = [ 
  2.   "17310727576501632001", 
  3.     "1081919648897631175", 
  4.     "4607248419625398332", 
  5.     "18158795172266376960", 
  6.     "18428747250223005711", 
  7.     "17294384420617192448", 
  8.     "216384094707056832", 
  9.     "13902625308286185532", 
  10.     "275821367043", 
  11.     "0", 
  12.     "0", 
  13.     "0", 
  14.     "0", 
  15.     "0", 
  16.     "0", 
  17.     "0", 

數(shù)組中的每一個元素可以轉成64位的二進制數(shù),二進制數(shù)中的每一位通過0和1表示一種權限,這樣每一個元素可以表示64種權限,整個usr_permission就可以表示16*64=1024種權限。后端之所以要對usr_permission進行壓縮,是因為后端采用的是微服務架構,各個模塊在通信的過程中通過在請求頭中加入usr_permission來做權限的認證。

數(shù)組usr_permission的第0個元素表示第[0, 63]號的權限,第1個元素表示第[64, 127]號的權限,以此類推。比如現(xiàn)在我們要查找第220號權限:

 
 
 
  1. const permission = 220 // 查看銷售出庫 
  2. const usr_permission = [ 
  3.   "17310727576501632001", 
  4.     "1081919648897631175", 
  5.     "4607248419625398332", 
  6.     "18158795172266376960", 
  7.     "18428747250223005711", 
  8.     "17294384420617192448", 
  9.     "216384094707056832", 
  10.     "13902625308286185532", 
  11.     "275821367043", 
  12.     "0", 
  13.     "0", 
  14.     "0", 
  15.     "0", 
  16.     "0", 
  17.     "0", 
  18.     "0", 
  19.  
  20. // "18158795172266376960" 表示第193號~第256號權限 
  21. // 1111 1100 0000 0000 1111 1111 1111 1111 1111 0000 0000 0011 1111 1111 0000 0000 
  22. // 220 % 64 = 28 
  23. // 0000 0000 0000 0000 0000 0000 0000 1111 1100 0000 0000 1111 1111 1111 1111 1111 
  24. // 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 
  25. // ------------------------------------------------------------------------------- 
  26. // 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 
  • 從usr_permission中我們得知第220號權限由第3個元素"18158795172266376960"表示。
  • 我們將"18158795172266376960"轉成二進制得到1111 1100 0000 0000 1111 1111 1111 1111 1111 0000 0000 0011 1111 1111 0000 0000。
  • 將220除以64得到余數(shù)28,也就是說二進制數(shù)1111 1100 0000 0000 1111 1111 1111 1111 1111 0000 0000 0011 1111 1111 0000 0000從右數(shù)的第28位表示第220號權限。
  • 我們可以將二進制數(shù)1111 1100 0000 0000 1111 1111 1111 1111 1111 0000 0000 0011 1111 1111 0000 0000右移28位,將表示第220號權限的位數(shù)推到最低位。
  • 然后將二進制數(shù)與1進行按位與操作,如果當前用戶擁有第220號權限,則最后得到的結果為1,反之為0。

以上就是前端查找權限的大致過程,那么這個代碼要怎么寫呢?在編寫代碼之前,我們先來復習一下JavaScript大數(shù)相關的知識,了解編寫代碼的過程中會遇到什么問題。

IEEE 754標準

在計算機組成原理這門課里我們學過,在以IEEE 754為標準的浮點運算中,有兩種浮點數(shù)值表示方式,一種是單精度(32位),還有一種是雙精度(64位)。

在IEEE 754標準中,一個數(shù)字被表示成 +1.0001x2^3 這種形式。比如說在單精度(32位)表示法中,有1位用來表示數(shù)字的正負(符號位),8位用來表示2的冪次方(指數(shù)偏移值E,需要減去一個固定的數(shù)字得到指數(shù)e),23位表示1后面的小數(shù)位(尾數(shù))。

比如0 1000 0010 0001 0000 0000 0000 0000 000,第1位0表示它是正數(shù),第[2, 9]位1000 0010轉換成十進制就是130,我們需要減去一個常數(shù)127得到3,也就是這個數(shù)字需要乘以2的三次方,第[10, 32]位則表示1.0001 0000 0000 0000 0000 000,那么這個數(shù)字表示的就是二級制中的 +1.0001*2^3 ,轉換成十進制也就是8.5。

同理,雙精度(64位)也是一樣的表現(xiàn)形式,只是在64位中有11位用來表示2的冪次方,52位用來表示小數(shù)位。

JavaScript 就是采用IEEE754 標準定義的64 位浮點格式表示數(shù)字。在64位浮點格式中,有52位可以表示小數(shù)點后面的數(shù)字,加上小數(shù)點前面的1,就有53位可以用來表示數(shù)字,也就是說64位浮點可以表示的最大的數(shù)字是 2^53-1 ,超過 2^53-1 的數(shù)字就會發(fā)生精度丟失。因為2^53用64位浮點格式表示就變成了這樣:

符號位:0 指數(shù):53 尾數(shù):1.000000...000 (小數(shù)點后一共52個0)

小數(shù)點后面的第53個0已經(jīng)被丟棄了,那么 2^53+1 的64位浮點格式就會變得和 2^53 一樣。一個浮點格式可以表示多個數(shù)字,說明這個數(shù)字是不安全的。所以在JavaScript中,最大的安全數(shù)是 2^53-1 ,這樣就保證了一個浮點格式對應一個數(shù)字。

0.1 + 0.2 !== 0.3

有一道很常見的前端面試題,就是問你為什么JavaScript中0.1+0.2為什么不等于0.3?0.1轉換成二進制是0.0 0011 0011 0011 0011 0011 0011 ... (0011循環(huán)),0.2轉換成二進制是0.0011 0011 0011 0011 0011 0011 0011 ... (0011循環(huán)),用64位浮點格式表示如下:

 
 
 
  1. // 0.1 
  2. e = -4; 
  3. m = 1.1001100110011001100110011001100110011001100110011010 (52位) 
  4.  
  5. // 0.2 
  6. e = -3; 
  7. m = 1.1001100110011001100110011001100110011001100110011010 (52位) 

然后把它們相加:

 
 
 
  1. e = -4; m = 1.1001100110011001100110011001100110011001100110011010 (52位) 
  2. e = -3; m = 1.1001100110011001100110011001100110011001100110011010 (52位) 
  3.  
  4. // 0.1和0.2指數(shù)不一致,需要進行對階操作 
  5. // 對階操作,會產生精度丟失 
  6. // 之所以選0.1進行對階操作是因為右移帶來的精度丟失遠遠小于左移帶來的溢出 
  7. e = -3; m = 0.1100110011001100110011001100110011001100110011001101 (52位) 
  8. e = -3; m = 1.1001100110011001100110011001100110011001100110011010 (52位) 
  9.  
  10.  
  11. e = -3; m = 10.0110011001100110011001100110011001100110011001100111 (52位) 
  12.  
  13. // 發(fā)生精度丟失 
  14. e = -2; m = 1.00110011001100110011001100110011001100110011001100111 (53位) 

我們看到已經(jīng)溢出來了(超過了52位),那么這個時候我們就要做四舍五入了,那怎么舍入才能與原來的數(shù)最接近呢?比如1.101要保留2位小數(shù),那么結果有可能是 1.10 和 1.11 ,這個時候兩個都是一樣近,我們取哪一個呢?規(guī)則是保留偶數(shù)的那一個,在這里就是保留 1.10。

回到我們之前的就是取m=1.0011001100110011001100110011001100110011001100110100 (52位)

然后我們得到最終的二進制數(shù):

1.0011001100110011001100110011001100110011001100110100 * 2 ^ -2

=0.010011001100110011001100110011001100110011001100110100

轉換成十進制就是0.30000000000000004,所以,所以0.1 + 0.2 的最終結果是0.30000000000000004。

BigInt

通過前面的講解,我們清晰地認識到在以前,JavaScript是沒有辦法對大于 2^53-1 的數(shù)字進行處理的。不過后來,JavaScript提供了內置對象BigInt來處理大數(shù)。 BigInt 可以表示任意大的整數(shù)??梢杂迷谝粋€整數(shù)字面量后面加 n 的方式定義一個 BigInt ,如: 10n ,或者調用函數(shù) BigInt() 。

 
 
 
  1. const theBiggestInt = 9007199254740991n; 
  2.  
  3. const alsoHuge = BigInt(9007199254740991); 
  4. // ? 9007199254740991n 
  5.  
  6. const hugeString = BigInt("9007199254740991"); 
  7. // ? 9007199254740991n 
  8.  
  9. typeof 1n === 'bigint'; // true 
  10. typeof BigInt('1') === 'bigint'; // true 
  11.  
  12. 0n === 0 // ? false 
  13.  
  14. 0n == 0 // ? true 

用BigInt實現(xiàn)的權限查找代碼如下:

 
 
 
  1. hasPermission(permission: Permission) { 
  2.     const usr_permissions = this.userInfo.usr_permissions 
  3.     const arr_index = Math.floor(permission / 64) 
  4.     const bit_index = permission % 64 
  5.     if (usr_permissions && usr_permissions.length > arr_index) { 
  6.       if ((BigInt(usr_permissions[arr_index]) >> BigInt(bit_index)) & 1n) { 
  7.         return true 
  8.       } 
  9.     } 
  10.     return false 

兼容分析

但是BigInt存在兼容性問題:

根據(jù)我司用戶使用瀏覽器版本數(shù)據(jù)的分析,得到如下餅狀圖:

不兼容BigInt瀏覽器的比例占到12.4%

解決兼容性的問題,一種方式是如果希望在項目中繼續(xù)使用BigInt,那么需要Babel的一些插件進行轉換。這些插件需要調用一些方法去檢測運算符什么時候被用于BigInt,這將導致不可接受的性能損失,而且在很多情況下是行不通的。另外一種方法就是找一些封裝大數(shù)運算方法的第三方庫,使用它們的語法做大數(shù)運算。

用第三方庫實現(xiàn)

很多第三方庫可以用來做大數(shù)運算,大體的思路就是定義一個數(shù)據(jù)結構來存放大數(shù)的正負及數(shù)值,分別算出每一位的結果再存儲到數(shù)據(jù)結構中。

jsbn 解決方案

 
 
 
  1. // yarn add jsbn @types/jsbn 
  2.  
  3. import { BigInteger } from 'jsbn' 
  4.  
  5. hasPermission(permission: Permission) { 
  6.     const usr_permissions = this.userInfo.usr_permissions 
  7.     const arr_index = Math.floor(permission / 64) 
  8.     const bit_index = permission % 64 
  9.     if (usr_permissions && usr_permissions.length > arr_index) { 
  10.       if ( 
  11.         new BigInteger(usr_permissions[arr_index]) 
  12.           .shiftRight(bit_index) 
  13.           .and(new BigInteger('1')) 
  14.           .toString() !== '0' 
  15.       ) { 
  16.         return true 
  17.       } 
  18.     } 
  19.     return false 
  20.   } 

jsbi 解決方案

 
 
 
  1. // yarn add jsbi 
  2.  
  3. import JSBI from 'jsbi' 
  4.  
  5. hasPermission(permission: Permission) { 
  6.     // 開發(fā)環(huán)境不受權限限制 
  7.     if (__DEVELOPMENT__) { 
  8.       return true 
  9.     } 
  10.  
  11.     const usr_permissions = this.userInfo.usr_permissions 
  12.     const arr_index = Math.floor(permission / 64) 
  13.     const bit_index = permission % 64 
  14.     if (usr_permissions && usr_permissions.length > arr_index) { 
  15.       const a = JSBI.BigInt(usr_permissions[arr_index]) 
  16.       const b = JSBI.BigInt(bit_index) 
  17.       const c = JSBI.signedRightShift(a, b) 
  18.       const d = JSBI.BigInt(1) 
  19.       const e = JSBI.bitwiseAnd(c, d) 
  20.       if (e.toString() !== '0') { 
  21.         return true 
  22.       } 
  23.     } 
  24.     return false 
  25.   } 

權限查找新思路

后來,一位同事提到了一種新的權限查找的解決方案:前端獲取到數(shù)組usr_permission以后,將usr_permission的所有元素轉成二進制,并進行字符串拼接,得到一個表示用戶所有權限的字符串permissions。當需要查找權限時,查找permissions對應的位數(shù)即可。這樣相當于在用戶進入系統(tǒng)時就將所有的權限都算好,而不是用一次算一次。

在中學時,我們學到的將十進制轉成二進制的方法是輾轉相除法,這里有一種新思路:

  • 比如我們要用5個二進制位表示11這個數(shù)
  • 我們需要先定義一個長度為5,由2的倍數(shù)組成的數(shù)組[16, 8, 4, 2, 1],然后將11與數(shù)組中的元素挨個比較
  • 11 < 16, 所以得到[0, x, x, x, x]
  • 11 >= 8,所以得到[0, 1, x, x, x],11 - 8 = 3
  • 3 < 4,所以得到[0, 1, 0, x, x]
  • 3 >= 2,所以得到[0, 1, 0, 1, x],3 - 2 = 1
  • 1>= 1,所以得到[0, 1, 0, 1, 1],1 - 1 = 0,結束
  • 所以用5位二進制數(shù)表示11的結果就是01011

根據(jù)上面的思路可以得到的代碼如下,這里用big.js這個包去實現(xiàn):

 
 
 
  1. import Big from 'big.js'     
  2.     import _ from 'lodash' 
  3.  
  4.     permissions = '' // 最后生成的權限字符串 
  5.  
  6.     // 生成長度為64,由2的倍數(shù)組成的數(shù)組 
  7.     generateBinaryArray(bits: number) { 
  8.       const arr: any[] = [] 
  9.       _.each(_.range(bits), (index) => { 
  10.         arr.unshift(Big(2).pow(index)) 
  11.       }) 
  12.       return arr 
  13.     }   
  14.  
  15.     // 將usr_permission中單個元素轉成二進制 
  16.     translatePermission(binaryArray: any[], permission: string) { 
  17.     let bigPermission = Big(permission) 
  18.     const permissionBinaryArray: number[] = [] 
  19.     _.each(binaryArray, (v, i) => { 
  20.       if (bigPermission.gte(binaryArray[i])) { 
  21.         bigPermission = bigPermission.minus(binaryArray[i]) 
  22.         permissionBinaryArray.unshift(1) 
  23.       } else { 
  24.         permissionBinaryArray.unshift(0) 
  25.       } 
  26.     }) 
  27.     return permissionBinaryArray.join('') 
  28.   } 
  29.  
  30.     // 將usr_permission中所有元素的二進制形式進行拼接 
  31.   generatePermissionString() { 
  32.     const usr_permissions = this.userInfo.usr_permissions 
  33.     let str = '' 
  34.     const binaryArray = this.generateBinaryArray(64) 
  35.     _.each(usr_permissions, (permission, index) => { 
  36.       str = `${str}${this.translatePermission(binaryArray, permission)}` 
  37.     }) 
  38.     this.permissions = str 
  39.   } 
  40.  
  41.     // 判斷時候擁有某項權限 
  42.   hasPermission(permission: Permission) { 
  43.     if (!this.permissions) { 
  44.       return false 
  45.     } 
  46.     return this.permissions[permission] === '1' 
  47.   } 

當前名稱:前端大數(shù)的運算及相關知識總結
網(wǎng)站網(wǎng)址:http://m.5511xx.com/article/codhojg.html