為什么80%的碼農都做不了架構師?>>> ??
最近看一篇js裝逼小技巧————雙波浪號的妙用(將內容轉化為數字,或者小數取整),但是本身我的JavaScript水平比較低對其底層操作和其使用范圍不甚了解;通過翻閱資料現進行簡單的整理。 ###裝逼技巧地址&截圖### 地址
###~~的本質### ~~被稱為“雙按位非”操作符。你通常可以使用它作為代替Math.trunc()的更快的方法。 一個按位非操作符~首先將輸入input截取為32位,然后將其轉換為-(input+1)。因此雙按位非操作符將輸入轉換為-(-(input + 1)+1),使其成為一個趨向于0取整的好工具。對于數字的輸入,它很像Math.trunc()。失敗時返回0,這可能在解決Math.trunc()轉換錯誤返回NaN時是一個很好的替代。
// 單個 ~
console.log(~1337)// -1338
// 數字輸入
console.log(~~47.11) // -> 47
console.`log`(~~1.9999) // -> 1
console.log(~~3) // -> 3
然而, 盡管~~可能有更好的性能,有經驗的程序員通常堅持使用Math.trunc()。要明白為什么,這里有一個關于此操作符的分析。 ###適用的情況### 當CPU資源很珍貴時
~~可能在各平臺上都比Math.trunc()快,但是你應該在你所關心的所有平臺上測試這種猜想。同樣,你通常需要執行數百萬這樣的操作來看看在運行時有沒有明顯的影響。
當不需要關心代碼清晰度時
如果你想迷惑其他人,或者想在minifier/uglifier時取得更大功效,這是一種相對廉價的方式。 ###禁用的情況### 當你的代碼需要維護時
代碼可讀性始終是最重要的。無論你工作在一個團隊,或是貢獻給開源倉庫,或是單飛。
當你忘記~~永遠趨向于0時
新手程序員或許更關注~~的聰明之處,卻忘記了“只去掉小數部分”的意義。這在將浮點數轉換為數組索引或關聯有序的值時很容易導致差一錯誤 ,這時明顯需要一個不同的取整方法。 (代碼可讀性不高往往會導致此問題)
打個比方,如果你想得到離一個數“最近的整數”,你應該用Math.round()而不是~~,但是由于程序員的惰性和每次使用需要敲10個鍵的事實,人類的手指往往會戰勝冷冷的邏輯,導致錯誤的結果。
相比之下,Math.xyz()(舉例)函數的名字清楚的傳達了它們的作用,減少了可能出現的意外的錯誤。
當處理大數時
因為~首先將數組轉換為32位,~~的結果偽值在 ±2.15*10^12左右。如果你沒有明確的檢查輸入值的范圍,當轉換的值最終與原始值有很大差距時,用戶就可能觸發未知的行為:
a = 2147483647.123 // 比32位最大正數,再多一點
console.log(~~a)// -> 2147483647 (ok)
a += 10000 // -> 2147493647.123 (ok)
console.log(~~a)// -> -2147483648 (huh?)
一個特別容易中招的地方是在處理Unix時間戳時(從1970年1月1日 00:00:00 UTC開始以秒測量)。一個快速獲取的方法:
epoch_int = ~~(+new Date() / 1000) // Date() 以毫秒計量,所以我們縮小它 然而,當處理2038年1月19日 03:14:07 UTC 之后的時間戳時(有時稱為Y2038 limit), 可怕的事情發生了:
// 2040年1月1日 00:00:00.123 UTC的時間戳
epoch = +new Date('2040-01-01') / 1000 + 0.123 // -> 2208988800.123
// 回到未來!
epoch_int = ~~epoch // -> -2085978496
console.log(new Date(epoch_int * 1000)) // -> Wed Nov 25 1903 17:31:44 UTC
// 這很搞笑,讓我們來取得正確結論
epoch_flr = Math.floor(epoch) // -> 2208988800
console.log(new Date(epoch_flr * 1000)) // -> Sun Jan 01 2040 00:00:00 UTC
當原始輸入的數據類型不確定時
因為~~可以將任何非數字類型轉換為0:
console.log(~~[]) // -> 0
console.log(~~NaN) // -> 0
console.log(~~null) // -> 0
一些程序員將其看作適當輸入驗證的替代品。然而,這將導致奇怪的邏輯問題,因此你不能辨別違法輸入還是真正的0。因此這并不推薦。 當很多人認為~~X == Math.floor(X)時
很多人由于很多原因錯誤的把”雙按位非”等同于Math.floor()。如果你不能準確地使用它,最終你很有可能會濫用它。
另一些人很細心的注意正數使用Math.floor()而負數使用Math.ceil(),但這又強制你在處理它的時候需要停下來想一想你處理的數是什么值。這又違背了使用~~快捷無陷阱的目的。
###結論###
- 謹慎使用。
- 在應用前檢查值。
- 仔細記錄被轉化值的相關假設。
- 審查代碼至少處理:
- 邏輯錯誤,不合法的輸入作為合法的0傳入其他代碼模塊
- 輸入轉換后范圍錯誤
- 錯誤的舍入方向導致差一錯誤