Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 肥女大bbwbbwbbwbbw…,欧美黄网站免费观看,日韩中文字幕精品免费一区

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          JavaScript位運算及其妙用

          JavaScript位運算及其妙用

          代計算機的數據是以二進制的形式進行存儲的。 位運算就是直接對整數在內存中的二進制位進行操作。使用位運算可以提高代碼性能,精簡代碼。

          在說位運算前,先簡單說說幾個概念,具體有機器數、真值、原碼、反碼、補碼

          機器數

          一個數在計算機中的二進制表示形式,叫做這個數的機器數。

          機器數是帶符號的,計算機中機器數的最高位是符號位, 正數為0, 負數為1。

          二進制的位數受機器設備的限制。機器內部設備一次能表示的二進制位數叫機器的字長,一臺機器的字長是固定的。字長8位叫一個字節(Byte),機器字長一般都是字節的整數倍,如字長8位、16位、32位、64位。

          例如在8位機器中,+3轉為二進制為 00000011,-3轉為二進制為 10000011,其中00000011和10000011就是機器數。

          機器數的真值

          直接用正號“+”和負號“-”來表示其正負的二進制數叫做機器數的真值。

          例如“00000011”和“10000011”是兩個機器數,而它們的真值分別為+0000011(即+3)和-0000011(即-3)。

          機器數包含原碼、反碼和補碼三種表示形式

          原碼

          將數的真值形式中“+”號用“0”表示,“-”號用“1”表示時,叫做數的原碼形式,簡稱原碼。

          也可理解為符號位加上真值的絕對值。第一位是符號位,其余位表示數值。

          例如:

          十進制

          真值

          原碼

          +3

          +0000011

          00000011

          -3

          -0000011

          10000011

          原碼的表示比較直觀,但是在進行加減法運算時,由于符號位的存在,使得機器的運算比較復雜,所以就引入了反碼和補碼。

          反碼

          對于正數來說,其反碼和原碼完全相同。

          對于負數來說,其反碼是在其原碼的基礎上, 符號位不變,其他位取反(0變1,1變0)。

          十進制

          真值

          原碼

          反碼

          +3

          +0000011

          00000011

          00000011

          -3

          -0000011

          10000011

          11111100

          補碼

          對于正數來說,其補碼和原碼完全相同。

          對于負數來說,其補碼是其反碼的末位加1,也就是說在其原碼的基礎上, 符號位不變,其他位取反后加1。

          十進制

          真值

          原碼

          反碼

          補碼

          +3

          +0000011

          00000011

          00000011

          00000011

          -3

          -0000011

          10000011

          11111100

          11111101

          注意:在計算機系統中,數值一律用補碼來表示和存儲。主要原因是為了便于處理和運算。

          具體為何需要引入反碼和補碼以及為何計算機系統中數值用補碼來表示和存儲,文章篇幅有限,此塊也不是本文的重點,詳細可以參考這篇文章www.cnblogs.com/zhangziqiu/…

          位運算符

          ? 在 JavaScript 中,數值是以64位浮點數的形式儲存。但是位運算符只對整數起作用。JavaScript在做位運算時,會事先將其轉換為32位有符號整型(用比特序列表示,即0和1組成)并開始計算,在得到結果后再將其轉回JavaScript的數值類型。

          ? 因為 js 的整數默認是帶符號數,所以在位運算中,只能使用 31 位,開發者是不能訪問最高位的。在運算時,如果位數超過,那么超過的數字會被丟棄,這會造成各種錯誤的結果,并且沒有任何警告信息。所以,并不是所有計算都適合通過位運算符來計算。

          ? 位運算符操作的是數值對應的機器數,這個機器數的表示形式就是補碼的形式。(上面說了,為了便于處理和運算,計算機系統中,數值用補碼來表示和存儲。)

          ? JavaScript中的位運算符有以下:

          • & :與運算;兩個運算比較的bit位都為1時,這個bit位才為1,只要有任意一個位是0,這個位就是0
          console.log(5 & 3) // 輸出1
          // 5的機器數表示為 00000000 00000000 00000000 00000101
          // 3的機器數表示為 00000000 00000000 00000000 00000011
          // 所以計算結果為1 00000000 00000000 00000000 00000001

          :或運算;兩個運算比較的bit位只要有一個是1,這個bit位就是1

          console.log(5 | 3) // 輸出7
          // 5的機器數表示為 00000000 00000000 00000000 00000101
          // 3的機器數表示為 00000000 00000000 00000000 00000011
          // 所以計算結果為7 00000000 00000000 00000000 00000111

          ^ :異或運算;兩個運算比較的bit位相同則為0,不同則為1

          console.log(5 ^ 3) // 輸出6
          // 5的機器數表示為 00000000 00000000 00000000 00000101
          // 3的機器數表示為 00000000 00000000 00000000 00000011
          // 所以計算結果為6 00000000 00000000 00000000 00000110

          ~:取反運算;0變1,1變0

          console.log(~5) // 輸出-6
          // 5的機器數表示為	00000000 00000000 00000000 00000101
          // 取反之后表示為	11111111 11111111 11111111 11111010
          // 該數是負數,負數補碼與原碼不同,先將其轉為原碼的反碼:11111111 11111111 11111111 11111001
          // 再轉為原碼:10000000 00000000 00000000 00000110
          // 所以結果為:-6

          << :左移;各二進位全部左移若干位,高位丟棄(即左邊超出的丟棄),低位補0(即右邊差的)

          console.log(5 << 3) // 輸出40
          // 5的機器數表示為  00000000 00000000 00000000 00000101
          // 左移三位結果為40 00000000 00000000 00000000 00101000

          圖示:


          console.log(-1073741822 << 2) // 輸出8
          // -1073741822的機器數表示為	 11000000 00000000 00000000 00000010		
          // 左移兩位結果為8            00000000 00000000 00000000 00001000

          圖示:

          >> :帶符號擴展右移;各二進位全部右移若干位,右邊被移出的丟棄,左邊會復制最左側的位來填充左側(這里最左側的位是符號位,也可以認為對于無符號數,高位補 0,對于有符號數,高位補符號位)。

          console.log(5 >> 2) // 輸出1
          // 5的機器數表示為	          00000000 00000000 00000000 00000101
          // 帶符號擴展右移兩位結果為1     00000000 00000000 00000000 00000001

          圖示:


          console.log(-5 >> 2) // 輸出-2
          // -5的機器數表示為:         11111111 11111111 11111111 11111011
          // 帶符號擴展右移兩位結果為:   11111111 11111111 11111111 11111110
          // 先將該值轉為原碼的反碼:    11111111 11111111 11111111 11111101
          // 再轉為原碼:		10000000 00000000 00000000 00000010
          // 所以結果為:-2

          圖示:

          >>> :無符號右移;各二進位全部右移若干位,右邊被移出的丟棄,左邊用0填充。因為符號位變成了 0,所以結果總是非負數。(即便右移 0 個比特,結果也是非負的。)

          console.log(5 >>> 2) // 輸出1
          // 5的機器數表示為	      00000000 00000000 00000000 00000101
          // 無符號右移兩位結果為1    00000000 00000000 00000000 00000001

          圖示:

          console.log(-5 >>> 2) // 輸出1073741822
          // -5的機器數表示為:                11111111 11111111 11111111 11111011
          // 無符號右移兩位結果為1073741822:    00111111 11111111 11111111 11111110


          位運算符的妙用

          位運算判斷奇數偶數

          根據數值的機器數最后一位是0還是1即可判斷,為0就是偶數,為1就是奇數。

          function fun(num) {
            if ((num & 1)===0) {
              return "偶數";
            }
            if ((num & 1)===1) {
              return "奇數";
            }
          }
          console.log(fun(44)); // 輸出偶數
          console.log(fun(55)); // 輸出奇數

          位運算實現兩數交換

          不使用第三個變量,實現兩個數交換,可以通過以下方式實現

          a=a + b;
          b=a - b;
          a=a - b;
          // 或者
          a=[b, (b=a)][0];
          // 或者
          [a, b]=[b, a]

          根據異或運算的規則,可以得出一些特性

          // a取任何值,下面的結果都成立
          a^a=0
          a^0=a
          // 異或運算且滿足交換律和結合律,即
          a^b=b^a
          a^b^c=a^(b^c)=(a^c)^b
          // 其實與運算也是滿足交換律和結合律的
          a&b=b&a
          a&b&c=a&(b&c)=(a&c)&b
          // 于是就可以推出
          a^b^a=b^(a^a)=b^0=b
          // 如果 c=a^b 那么 a=c^b,這是因為 a=c^b=a^b^b=a^(b^b)=a^0=a
          // 如果 d=a^b^c 那么 a=d^b^c, 以此類推...

          那么根據以上的特性,就可以用位運算的方式實現兩數交換,如下

          a=a^b
          b=a^b
          a=a^b

          位運算實現乘除法

          將某個數值帶符號右移一位,相當于該數值除以2;

          將某個數值左移一位,相當于該數值除以2;

          console.log(6 >> 1) // 輸出3
          console.log(6 << 1) // 輸出12

          而右移n位,就相當于該數值除以2的n次方

          而左移n位,就相當于該數值乘以2的n次方

          console.log(32 >> 3) // 輸出4, 相當于32除以2的3次方(8),等于4
          console.log(32 << 3) // 輸出256, 相當于32乘以2的3次方(8),等于256

          但是用這種用法實現乘除法有很多問題,比如

          • 結果只會保留整數部分
          console.log(7 >> 1) // 輸出3

          如果數值操作影響到了符號位,結果會錯誤

          console.log(-2147483646 << 1) // 輸出4, 而-2147483646除以2的結果應該是-1073741823

          位元算實現取整

          • 通過或運算實現:將數值與0進行或運算從而得到該數值的整數部分
          console.log(5.2 | 0) // 輸出5
          console.log(-7.2 | 0) // 輸出-7

          通過與運算實現:將數值與-1進行或運算從而得到該數值的整數部分

          console.log(5.2 & -1) // 輸出5
          console.log(-7.2 & -1) // 輸出-7

          通過異或運算實現:

          • 將數值與0進行異或運算
          console.log(5.2 ^ 0);// 輸出5
          console.log(-7.2 ^ 0);// 輸出-7

          將數值與另一個數進行兩次異或運算

          console.log(5.2 ^ 2 ^ 2); // 輸出5
          console.log(-7.2 ^ 2 ^ 2);// 輸出-7

          通過取反運算實現:將數值進行兩次取反運算

          console.log(~~5.2); // 輸出5
          console.log(~~-7.2); // 輸出-7

          通過左移或右移運算實現:將數值左移或右移0位

          console.log(5.2 >> 0); // 輸出5
          console.log(-7.2 << 0); // 輸出-7

          位掩碼和權限管理

          位掩碼(BitMask),是”位(Bit)“和”掩碼(Mask)“的組合詞?!蔽弧爸复M制數據當中的二進制位,而”掩碼“指的是一串用于與目標數據進行按位操作的二進制數字。組合起來,就是”用一串二進制數字(掩碼)去操作另一串二進制數字“的意思。

          位掩碼可以用于多種場景,比較常用的,我們可以用它來進行權限管理。

          假設我們在做一個直播教學系統,每個學生都有其自己的狀態,比如是否舉手,攝像頭狀態,設備類型三個狀態。

          如果我們給每個學生設置三個狀態值,那會很麻煩(這里只是舉例三個,現實場景可能有七八個狀態值)

          那么我們可以設計每個學生一個狀態碼,例如這個狀態碼是一個9位的二進制數

          我們可以這個數上劃分每個狀態所占的位

          例如從最低位(按最低位0)開始

          • 低二位(從最低位(0)到第1位)表示學生的舉手狀態。舉手的話為 01,未舉手為 10,未知為 00
          • 從低位第二位到第四位表示該學生的攝像狀態。打開為001,關閉為010,未知為000,設備故障為011,被禁用為100
          • 從低位第五位到第八位表示該學生的設備類型。例如window是0001,mac是0010,ipad是0011,android是0100,ios是0111,未知是0000

          具體可以在代碼中定義一個枚舉:

          const enums={
            // 舉手狀態
            handsUp: {
              open: 0b01, // 舉手中
              close: 0b10, // 未舉手
              unknow: 0b00, // 未知
              lowBitPos: 0, // 從第幾位開始使用
              bitLength: 2, // 所占位的長度
            },
            // 攝像頭狀態
            camera: {
              open: 0b001, // 打開
              close: 0b010, // 關閉
              disable: 0b011, // 設備故障
              ban: 0b100, // 被禁用
              unknow: 0b000, // 未知
              lowBitPos: 2, // 從第幾位開始使用
              bitLength: 3, // 所占位的長度
            },
            // 設備類型
            device: {
              window: 0b0001, // window電腦
              mac: 0b0010, // 蘋果電腦
              ipad: 0b0011, // 蘋果平板
              android: 0b0100, // 安卓手機
              ios: 0b0111, // 蘋果手機
              unknow: 0b0000, // 未知
              lowBitPos: 5, // 從第幾位開始使用
              bitLength: 4, // 所占位的長度
            }
          };

          例如我隨便舉個例子,假設某個學生的狀態碼為 0b001001001

          那么其對應狀態如下


          但是在代碼中,我們要如何獲取或修改學生狀態對應的值呢?這就要用到前面所說的位掩碼的知識了

          獲取

          首先是獲取狀態值,我們需要從該狀態碼中截取出指定的從某一位到某一位的值。

          // 舉個例子,假設學生此時的狀態碼為0b001001001,獲取該學生的攝像頭狀態
          let state=0b001001001
          // 因為攝像頭狀態是從低位開始(0開始)第二位到第四位
          // 首先將狀態碼右移兩位
          let v=state >> enums.camera.lowBitPos  // enums上面定義了
          // 然后將轉換后的值與0b111進行與運算
          let result=v & 0b111
          console.log(result) // 輸出結果為3,也就是0b010,如此便獲取到了該學生的攝像頭狀態

          上面代碼中,因為攝像頭狀態是占了三個bit位,所以需要與0b111進行與運算來得到結果,但是舉手狀態是兩位,設備類型是四位,就要分別和0b11和0b1111進行與運算,這樣比較麻煩,所以上面的代碼可以優化為

          let result=v & ((0b1 << enums.camera.bitLength) - 1)

          也可以將所有需要用到的位全是1的值先存在數組里面,方便后續運算

          const bitArr=[0b0]
          // 但位運算時僅支持 1 << 31大小
          for (let i=1; i < 32; i++) {
              bitArr.push((0b1 << i) - 1)
          }
          Object.freeze(bitArr) // 凍結數組  不允許修改
          
          let result=v & bitArr[enums.camera.bitLength]

          總結上面代碼,我們可以將獲取狀態值封裝為一個函數

          const getProvide=(state, lowBitPos, bitLength)=> {
             return (state >> lowBitPos) & bitArr[bitLength]
          }

          修改

          知道如何獲取以后,那么修改該如何呢

          例如,目前學生的狀態碼對應的設置狀態為0b0010(蘋果電腦),我們要修改學生的設備狀態為0b0100(安卓手機)

          let state=0b001001001
          // 首先先獲取設備狀態對應的值
          let deviceState=getProvide(state, enums.device.lowBitPos, enums.device.bitLength)
          // 然后先清除設備狀態對應的位數區間
          let clearState=state - (deviceState << enums.device.lowBitPos)
          // 然后重新加上新的狀態區間即可
          let result=clearState + (enums.device.android << enums.device.lowBitPos)
          console.log(result.toString(2)) // 輸出 010001001

          封裝為函數如下

          const setProvide=(state, lowBitPos, bitLength, newSingleState)=> {
            const get=getProvide(state, lowBitPos, bitLength)
            let clearState=state - (get << lowBitPos)
            return clearState + (newSingleState << lowBitPos)
          }

          如此便實現了對學生用戶的權限狀態控制。

          者:平臺前端組

          轉發鏈接:https://mp.weixin.qq.com/s/MbSTkkzf7nDjtBkmh7Iqbw

          者:Dunizb

          轉發鏈接:https://mp.weixin.qq.com/s/kxo6tOmaJYdINiIOqls1LA


          主站蜘蛛池模板: 麻豆文化传媒精品一区二区 | 午夜一区二区免费视频| 亚洲熟妇av一区| 国产一区二区精品在线观看| 国产AV一区二区三区传媒| 亚洲中文字幕丝袜制服一区 | 精品人妻AV一区二区三区| 中文字幕人妻丝袜乱一区三区| 性色av无码免费一区二区三区| 99久久精品午夜一区二区| 中文字幕AV一区中文字幕天堂| 久久精品无码一区二区三区日韩| 亚洲AV无码一区二区三区网址 | 97一区二区三区四区久久| 中文字幕在线无码一区| 精品国产AⅤ一区二区三区4区| 国产一区在线视频观看| 精品乱人伦一区二区| 日本美女一区二区三区| 一区二区三区在线观看免费| 一区二区三区电影在线观看| 国产精品久久亚洲一区二区| 国产精品视频免费一区二区三区| 无码中文字幕一区二区三区| 日本片免费观看一区二区| 极品人妻少妇一区二区三区| 久久中文字幕无码一区二区| 亚洲AV成人精品日韩一区| 国产伦精品一区二区三区免.费| 日韩人妻无码免费视频一区二区三区| 精品国产免费一区二区| 午夜精品一区二区三区在线视| aⅴ一区二区三区无卡无码| 国产av天堂一区二区三区| 国精产品一区二区三区糖心| 无码免费一区二区三区免费播放| 人妖在线精品一区二区三区| 国产微拍精品一区二区| 波多野结衣一区在线观看| 亚洲乱码国产一区网址| 国产美女精品一区二区三区|