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

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

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

 背景

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

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

這個(gè)usr_permission是一個(gè)長度為16的大數(shù)字符串?dāng)?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ù)組中的每一個(gè)元素可以轉(zhuǎn)成64位的二進(jìn)制數(shù),二進(jìn)制數(shù)中的每一位通過0和1表示一種權(quán)限,這樣每一個(gè)元素可以表示64種權(quán)限,整個(gè)usr_permission就可以表示16*64=1024種權(quán)限。后端之所以要對usr_permission進(jìn)行壓縮,是因?yàn)楹蠖瞬捎玫氖俏⒎?wù)架構(gòu),各個(gè)模塊在通信的過程中通過在請求頭中加入usr_permission來做權(quán)限的認(rèn)證。

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

 
 
 
  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號權(quán)限 
  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號權(quán)限由第3個(gè)元素"18158795172266376960"表示。
  • 我們將"18158795172266376960"轉(zhuǎn)成二進(jìn)制得到1111 1100 0000 0000 1111 1111 1111 1111 1111 0000 0000 0011 1111 1111 0000 0000。
  • 將220除以64得到余數(shù)28,也就是說二進(jìn)制數(shù)1111 1100 0000 0000 1111 1111 1111 1111 1111 0000 0000 0011 1111 1111 0000 0000從右數(shù)的第28位表示第220號權(quán)限。
  • 我們可以將二進(jìn)制數(shù)1111 1100 0000 0000 1111 1111 1111 1111 1111 0000 0000 0011 1111 1111 0000 0000右移28位,將表示第220號權(quán)限的位數(shù)推到最低位。
  • 然后將二進(jìn)制數(shù)與1進(jìn)行按位與操作,如果當(dāng)前用戶擁有第220號權(quán)限,則最后得到的結(jié)果為1,反之為0。

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

IEEE 754標(biāo)準(zhǔn)

在計(jì)算機(jī)組成原理這門課里我們學(xué)過,在以IEEE 754為標(biāo)準(zhǔn)的浮點(diǎn)運(yùn)算中,有兩種浮點(diǎn)數(shù)值表示方式,一種是單精度(32位),還有一種是雙精度(64位)。

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

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

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

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

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

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

0.1 + 0.2 !== 0.3

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

 
 
 
  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ù)不一致,需要進(jìn)行對階操作 
  5. // 對階操作,會產(chǎn)生精度丟失 
  6. // 之所以選0.1進(jìn)行對階操作是因?yàn)橛乙茙淼木葋G失遠(yuǎn)遠(yuǎn)小于左移帶來的溢出 
  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位),那么這個(gè)時(shí)候我們就要做四舍五入了,那怎么舍入才能與原來的數(shù)最接近呢?比如1.101要保留2位小數(shù),那么結(jié)果有可能是 1.10 和 1.11 ,這個(gè)時(shí)候兩個(gè)都是一樣近,我們?nèi)∧囊粋€(gè)呢?規(guī)則是保留偶數(shù)的那一個(gè),在這里就是保留 1.10。

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

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

1.0011001100110011001100110011001100110011001100110100 * 2 ^ -2

=0.010011001100110011001100110011001100110011001100110100

轉(zhuǎn)換成十進(jìn)制就是0.30000000000000004,所以,所以0.1 + 0.2 的最終結(jié)果是0.30000000000000004。

BigInt

通過前面的講解,我們清晰地認(rèn)識到在以前,JavaScript是沒有辦法對大于 2^53-1 的數(shù)字進(jìn)行處理的。不過后來,JavaScript提供了內(nèi)置對象BigInt來處理大數(shù)。 BigInt 可以表示任意大的整數(shù)??梢杂迷谝粋€(gè)整數(shù)字面量后面加 n 的方式定義一個(gè) BigInt ,如: 10n ,或者調(diào)用函數(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實(shí)現(xiàn)的權(quá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%

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

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

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

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)境不受權(quá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.   } 

權(quán)限查找新思路

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

在中學(xué)時(shí),我們學(xué)到的將十進(jìn)制轉(zhuǎn)成二進(jìn)制的方法是輾轉(zhuǎn)相除法,這里有一種新思路:

  • 比如我們要用5個(gè)二進(jìn)制位表示11這個(gè)數(shù)
  • 我們需要先定義一個(gè)長度為5,由2的倍數(shù)組成的數(shù)組[16, 8, 4, 2, 1],然后將11與數(shù)組中的元素挨個(gè)比較
  • 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,結(jié)束
  • 所以用5位二進(jìn)制數(shù)表示11的結(jié)果就是01011

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

 
 
 
  1. import Big from 'big.js'     
  2.     import _ from 'lodash' 
  3.  
  4.     permissions = '' // 最后生成的權(quán)限字符串 
  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中單個(gè)元素轉(zhuǎn)成二進(jìn)制 
  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中所有元素的二進(jìn)制形式進(jìn)行拼接 
  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.     // 判斷時(shí)候擁有某項(xiàng)權(quán)限 
  42.   hasPermission(permission: Permission) { 
  43.     if (!this.permissions) { 
  44.       return false 
  45.     } 
  46.     return this.permissions[permission] === '1' 
  47.   } 

分享標(biāo)題:前端大數(shù)的運(yùn)算及相關(guān)知識總結(jié)
轉(zhuǎn)載注明:http://www.5511xx.com/article/codhojg.html