js >>> 0 談談 js 中的位運算

晚上在讀 lodash 源碼的時候,看到 baseSlice 中有這樣一行代碼:
start >>>= 0;

這不就是無符號右移嘛,當時第一感覺是是為了取絕對值,后來發現并不是,嘗試了多次之后,發現情況有點詭異啊,我們使用 chrome 調試工具運行一下 js 中的無符號右移 0 位。

chrome console

不僅是 null 無符號右移會變成 0 ,js 中的其他非數值做此運算都會變成 0 。

chrome console

接下來我們來看看為什么會這樣(事實上不僅僅只是無符號右移是這樣)。要理解這個問題需要先明白什么是位運算以及為什么需要位運算,然后搞明白 js 中的位運算有什么特別之處。

什么是位運算?

敬請期待

js 中的位運算有什么特別之處呢?

(這一部分我是拿 java、go 與 js 做對比的。)

1 js 中為什么浮點數也能參與位運算

這在 java、go、c 中都是不被允許的

* 6 種 基本類型:
    1. Boolean
    2. Null
    3. Undefined
    4. Number
    5. String
    6. Symbol (ECMAScript 6 新定義)

細心的人已經發現,基本類型里并沒有浮點型。

事實上在 js 中的 Number 類型是不區分 int、long、float、double 類型的(go 的用戶們就呵呵一笑了,來來來,我們的浮點型就能王炸你)。回正題,不區分整型、浮點型那怎么存儲呢,為了不丟失精度, js 中的 Number 類型實際上一個基于 IEEE 754 標準的雙精度64位浮點數(java 的同學就把它當成 double 看)。看到這我想很多人應該能明白為什么 js 里浮點數也能參與位運算了吧。這也是沒有辦法,因為對于內存來說整型、浮點型都沒有區別了。

這里是有一個問題的,因為當 js 需要進行位運算時,會將操作數通通轉成 32 位比特序列(0,1),也就是補碼。操作完成之后,再按照 64 位浮點數存儲

2 那么 js 做位運算時,小數部分怎么處理呢?

注意這里說的全部位運算

直接丟棄!!! 曾吶!這么虎?

沒錯,就是這么暴力,那么問題來了,既然小數部分不參與位運算,那么為什么不能像 java、go 那樣直接禁止呢?關于這個問題,我想那就是語言設計者的想法,我就不知道了。但是這其實也帶來了一些特別的操作,比如在 js 中雙取反是可以做取整操作的。

~~2.2 // 2
~~2.8 // 2

3 js 中非數值類型如何進行位運算的呢?

js 需要進行位運算的時候,對于非數值類型,會首先將操作數轉成一個整型(就是0)然后在進行運算。這就解釋了為什么 js 中可以允許非數值類型參與運算,其實這是個偽命題,因為實質上是對非數值操作數的整型表達式進行的位運算。

這里需要注意,上面說過了 js 中的整型在內存中都是一個 64 位雙精度浮點型,但是 js 進行位運算時,會將操作數轉成帶符號位的 32 位比特序列(0,1),也就是補碼。運算結束后,再按照 64 位存儲。那么問題來了,這里肯定會存在精度丟失對吧,這應該不難理解。js 確實也是這樣處理的,超過 32 位的部分直接截斷。

所以對一個非數值變量做取反操作,得到的一定是 -1,因為實際上等于對 0 做取反操作。

chrome console

4 js 中的無符號右移到底有什么特別之處?

首尾呼應一下,畢竟就是這個問題使我查資料寫了這篇文章。

首先解釋一下,>>> 無符號右移原本是 java 里特有的(這里是和 js、go 對比,其他語言我沒用過,不能亂說)。js 中的無符號右移跟 java 幾乎一樣,除了一點兩種語言處理方式完全不一樣。

那就是并沒有真正發生移位的情況下,符號位會不會被替換成0。java 中是不會替換的,但是 js 中是會發生替換的。

當操作數是正數的時候,不管有沒有真的移位并沒有區別,因為正數的符號位是 0。
當操作數是負數時,移動位數大于0,也體現不出區別:

// java
5 >>> 0  // 5
5 >>> 1  // 2
-1 >>> 1 // 2147483647

// js
5 >>> 0  // 5
5 >>> 1  // 2
-1 >>> 1 // 2147483647

但是當操作數是負數,無符號右移 0 位時,區別就大了:

// java
-1 >>> 0 // -1

// js
-1 >>> 0 // 4294967295

這是因為 -1 的補碼是:

11111111111111111111111111111111

>>>0 實際上并沒有發生數位變化,但是 js 卻會把符號位替換成 0,

// java
11111111111111111111111111111111

// js
01111111111111111111111111111111

此時原來負數的補碼,變為了正數的源碼(這就是為什么 js 中 -1>>>0 會變成一個巨大的正整數)。

js 中無符號右移時,不管正數、負數都會首先將符號位替換成 0,然后再進行移位。也就是說,該運算符永遠返回正整數。

總結

js 的位運算,為什么會有這么多奇怪的地方呢?我相信很多同學都會有這種想法,特別是 java 的同學們吧。為此我查了 js 的歷史。

1995 Sun 公司正式發布 java 語言,當時的網景公司正在為它們的 Navigator 瀏覽器尋找一種網頁腳本(此前的瀏覽器不具備互動能力)。當他們看到 Sun 公司的宣傳后,與 Sun 合作開發全新的腳本語言 javascript 。此前我一直不明白 js 既然不是 java 的腳本,為什么叫這個名字。現在懂了,因為當時新腳本語言的決策中,Sun 公司占了很大一環。

1995年5月 按照公司的要求(一個像 java 但是比 java 簡單的腳本語言),Brendan Eich 僅用10天就寫出了 javascript

在我們膜拜大神的時候,也要認清一個現實,當時給 Brendan Eich 的時間太短了,所以很多問題并沒有很好的解決,而且一邊模仿 java、c,一邊還要簡化數據類型、內存模型。我覺得這就是為什么 js 的位運算這么奇怪的原因。

js 完全套用了 java 的位運算符。
但是 java 的位運算是針對整數的,對 js 沒什么用啊,因為 js 中,所有數字都保存為雙精度浮點型。如果使用它們的話,js 不得不將操作數先轉為整數,然后再進行運算。

所以很多人不建議在 js 中使用位運算,理由是 js 天生就會進行類型轉換,使得效率降低。

到底在項目中要不要用位運算,我覺得效率啦、存儲空間啦都不是問題,重要的是可讀性,如果你的團隊成員都比較擅長位運算,那就可以用(其實很多時候位運算更方便業務的實現跟理解,比如開關量,可以用與或非很快的表達出一些復雜的邏輯判斷)。反過來,如果大家連二進制存儲都整不明白,那就不要用,不然以后沒人懂你寫了什么。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容

  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執行單位為行(line),也就是一...
    悟名先生閱讀 4,185評論 0 13
  • 一、ECMAScript 一元運算符 一元運算符只有一個參數,即要操作的對象或值。它們是 ECMAScript 中...
    耦耦閱讀 545評論 0 0
  • 我們一路撕心吶喊,搖滾落寞,吧嗒一聲,子彈還在飛,人已到中年。 要命的是這種察覺很唐突,就像是...
    水煮蓮花閱讀 722評論 6 14