關于js中的`~~`和`!!`

國家地理

首頁圖來自看大圖,侵刪。



今天看到這樣一段代碼:

// JavaScript代碼
if ( !~items.indexOf( item ) ) {  
    items.push(item);  
}  

!~ 是什么最新操作?于是花了一些時間查找了相關資料學習了一下。

先上干貨,結論如下:

事實上,這是兩個運算,第一個運算是!,js中代表邏輯非;第二個運算是~,意義為按位非

上面那個例子,是在items這個數組中查找元素item的下標,使用indexOf()函數。

ps: 其實不僅在js中有這種語法,其他任何語言也都會有。

  • 如果在集合中查找到了item,則該函數返回對應下標,是一個大于0的整數,該整數按位非的結果一定不為0,取邏輯非后,表達式結果為假。
  • 如果在集合中沒找到item,則該函數返回-1這個值。而恰好,-1這個值按位非的結果剛好是0,再取邏輯非后,表達式結果為真。

所以前面提到的代碼的含義為,如果在items中沒有item這個元素,就添加到items中。

有貓病吧?

可能有些人看到這里就怒了!欺負我智商不夠嗎?這種簡單的功能,我分分鐘就能寫出來好嗎?走你:

// 方法1
if ( items.indexOf( item ) === -1) {  
    items.push(item);
}
// 方法2
if ( tems.indexOf( item ) < 0) {  
    items.push(item);  
}

扎心了,老鐵,這三種方法實現的功能都是一樣的好嗎?那為什么還要使用這種晦澀難懂的語法呢?我們可以揣摩一下這段代碼的作者的心里活動如下:

這種高級語法,其他人做得到嗎???(叉會兒腰)

好吧,我們設想一下,也許是效率問題?這種語法更接近底層,所以執行效率更快?根據相關資料和測試按位非的寫法似乎也并沒有明顯的效率提升,反而還不如平時我們寫的邏輯判斷。ps:此處存疑。
所以,如果是日常使用的話,不知道~是什么操作也完全OK。不想知道~是什么的話,閣下可以關掉這個頁面了。


什么是按位非~?

好吧,我們來剖析一下這里面的門道,看一下按位非操作是什么樣子的。在深入之前,我們需要先回顧一下相關知識。

原碼、反碼、補碼

來復習一下計算機基礎,數字在計算機中是以二進制的形式存在的,那具體的存放規則又是什么呢?此處我們立一個大前提,假設我們所處的環境是8位機,并且先只考慮整數的情況。具體來看一下:

數字4用二進制表示是100。由于是正數,首位字符位是0,所以補全位數是0000 0100,這個就是數字4的原碼。由于正數的原碼補碼反碼都相等,所以數字4的反碼和補碼也是0000 0100

如果是負數呢?比如數字-4,應先取正數的原碼,即0000 0100,然后將首位(符號位)變為1,代表這是負數,所以我們得到了數字-4的原碼是1000 0100。然后將除了首位(符號位)的其他位都取相反的值,得到反碼:1111 1011,最后加上1,得到數字-4的補碼:11111100,所以我們得到以下這個表:

數字 4 -4
原碼 0000 0100 1000 0100
反碼 0000 0100 1111 1011
補碼 0000 0100 1111 1100

小結一下,原碼到補碼的步驟:

  • 1, 原碼取反(除了首位),得到反碼
  • 2, 反碼+1,得到補碼

這個步驟一會兒還要用到,先記一下。

而在計算機中,為了運算簡便(只需要一套電路),數字的存儲都是存儲的補碼,所以數字-4在存儲單元中的值并不是它的原碼1000 0100,而是它的補碼,即1111 1100。同樣的,數字4存放的也不是二進制0000 0100,而是它的補碼:0000 0100(由于是正數,所以這兩個值相同,但不應該理解為單純地存儲二進制數)。

~運算

OK,現在我們有了理論的基礎,我們再來討論按位非~運算。

~是一個單目運算符,它的定義是這樣的:

表達式中的任何一位為 1,則結果中的該位變為 0。 表達式中的任何一位為 0,則結果中的該位變為 1。 --------摘自MSDN

也就是說,~運算的過程是這樣的,將要運算的數轉換為補碼,然后所有值為0的位變成1,值為1的位變為0。即:

var m = ~3; // 對數字3執行 按位非 運算
// 3 在計算機中存儲的值為 0000  0011
// 按位非之后,變成了 1111 1100
// 請記住這是一個補碼,它代表的是十進制的數字 -4(上面的表格↑),所以m的值是 -4
 console.log(m); //  -4

那這個值怎么計算呢?我們再來一次,計算~25的值:

var n = ~25; // 對數字25執行 按位非 運算
// 25的補碼是 0001 1001
// 按位非之后,1110 0110
// 這個就是計算的結果,這個結果是一個補碼。但是這個補碼怎么轉換為十進制呢?我們可以將原碼到補碼的計算過程倒過來進行計算。只要通過這個補碼得到原碼,就知道十進制是多少了。

// 補碼-1,得到反碼
// 反碼按位取反(除了首位),得到原碼

// 1110 0110    <-- 補碼
// 補碼減1 得到1110 0101 <--  反碼
// 除了首位,其他位按位取反
// 得到1001 1010 <-- 原碼
// 觀察首位,為1,表示這是負數,除去首位,剩下的二進制為 11010 ,即26
// 所以結果為 -26
console.log(n); // -26

另外,就像左移運算<<、右移>>等按位運算符在其他語言里一樣,~運算在其他語言里也是可以使用的,使用方法完全相同。

~~運算

由上面的例子擴展一下,如果是兩次取反,自然結果就變回來啦。特別要注意的是如果~后面的表達式不是int值,而是bool值或者字符串或者其他值得話,計算機會把表達式強制轉換為int再計算。

也就是說,~~會把后面的表達式強行變成int。

var n = ~~5; // 5
var m = ~~-8; // -8
var j = ~~true; // 將true轉換為int,也就是1,然后再計算。結果為1

!!運算

講到這里不得不提一下以前經常使用的!!運算符,這個運算可以把表達式強行轉換為邏輯值,這個和上面提到的~~類似。這種小技巧一樣適合其他語言。

if ( !!localStorage.getItem( "highScore" ) ){
    localStorage.setItem( "highScore", "0" );
}

寫在最后

寫到這里特別想感慨一下,每個coder都有各自的編碼習慣,這種習慣一旦養成,想要改變是非常難的,所以希望大家在使用這些技巧之前,要考慮這種技巧的優劣,以免養成了不好的習慣,很難糾正掉。我用到這些奇巧淫技的地方是非常少的,理由很簡單,我不希望別人閱讀我的代碼非常吃力。像這種C語言風格的代碼也許本就不應該出現在js這種面向對象語言中吧。

閱讀別人的代碼也能夠看出別人的性格特點,如果別人寫出這樣的代碼給我閱讀,我可能會覺得這個人非常特立獨行。而我們公司也許并不需要這種特立獨行,作為研究學習尚可,但是實際應用中,還是希望閱讀這篇文章的閣下,請避免寫出這樣的代碼。

所以我是反對過度使用奇巧淫技的。

啰嗦

由于在下水平有限,可能在文中有多處表達錯誤或不準確的地方。如果閣下在文章里發現在下寫的有失水準,還請不吝賜教,在評論指出。轉載請注明出處。如果希望鼓勵一下作者,點個贊就好。

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

推薦閱讀更多精彩內容

  • 網站亂碼問題我們會經常碰到,大多見于非英文的中文字符或其他字符亂碼,而且,這類問題常常是因為編碼方式問題,主要原因...
    波段頂底閱讀 2,938評論 1 9
  • 1.編譯程序(1)gcc xx.c,他會默認生成一個a.out的可執行文件,在a.out所在目錄,執行./a.o...
    萌面大叔2閱讀 1,314評論 0 1
  • Java源碼 Integer Integer的簽名如下,繼承了Number類并實現Comparable接口 Com...
    wngn123閱讀 1,256評論 0 2
  • C語言基礎 編譯程序 gcc xx.c,他會默認生成a.out的可執行文件,在a.out所在目錄,執行./a.ou...
    帥碧閱讀 644評論 1 3
  • 1.編譯程序 (1)gcc xx.c,他會默認生成一個a.out的可執行文件,在a.out所在目錄,執行./a....
    萌面大叔2閱讀 495評論 0 1