詳細談談JS中的內存與變量存儲
前言
在前端領域,因為大部分在跟UI打交道,內存管理是最容易被忽略的部分。如果不懂內存,就看不清很多問題的本質,也難以寫出更合格的代碼,本次帶大傢走進內存的世界。
JS神奇的Number
案例一:金額的計算與傳遞
18.9 * 100 =1889.9999999999998
案例二:違背的數學定律
0.1 + 0.2 === 0.3 // false (function (a, b, c) { return a + b + c === a + ( b + c ) })(0.1, 0.2, 0.3) // false
案例三:無限循環的加法
(function (num) { while(true) { if (++num % 13 === 0) { return num } } })(2 ** 53)
案例四:JSON.parse
JSON.parse('{"a":180143985094813214124}') //{a: 180143985094813220000}
通過上面的四個案例我們可以看出,數字在計算機中運算往往會給人帶來一些“驚喜”,要想防止這些意想不到的結果,我們首先要瞭解Number在Javascript中到底是怎麼存儲的?
存儲數字
計算機是用二進制來存儲數據的,所以數字也需要轉換成相應二進制: 000 或者 111 的不同組合序列。
二進制如何轉換
如何將一個數字轉換成二進制,這裡舉個例子說明一下:
把十進制小數 106.6953125106.6953125106.6953125 轉換成二進制
遇到小數轉換時,需要把整數和小數兩部分分別進行處理,整數 106106106 除以 222 直到商是 000 為止,取每次除 222 得到的餘數結果
106 / 2 = 53 ...... 0 53 / 2 = 26 ...... 1 26 / 2 = 13 ...... 0 13 / 2 = 6 ...... 1 6 / 2 = 3 ...... 0 3 / 2 = 1 ...... 1 1 / 2 = 0 ...... 1 結果為得到的餘數按照從右往左排列 1101010
小數 0.69531250.69531250.6953125 乘以 222 直到不存在小數位為止,並計下每次乘後的整數位結果,
0.6953125 x 2 = 1.390625 ...... 1 0.390625 x 2 = 0.78125 ...... 0 0.78125 x 2 = 1.5625 ...... 1 0.5625 x 2 = 1.125 ...... 1 0.125 x 2 = 0.25 ...... 0 0.25 x 2 = 0.5 ...... 0 0.5 x 2 = 1 ...... 1 結果為得到的整數位按照從左往右排列 1011001
將計算後的 000 111 序列拼在一起就得到轉換的二進制 1101010.10110011101010.10110011101010.1011001,用科學計數法表示為1.1010101011001∗261.1010101011001*2^61.1010101011001∗26,算出瞭二進制,接下來需要將它存進計算機中,在Javascript中不區分整數和小數,數字統一按照雙精度浮點數的要求來存儲,主要包含下面規則:
- 使用 8bytes(64bits)8bytes(64bits)8bytes(64bits) 存儲雙精度浮點數
- 存儲小數用科學計數法表示的數據
- 第一位表示符號,後 111111 位表示指數,指數按照補位運算,即直接 102310231023 加指數位
- 剩餘 525252 位表示小數點後的尾數,超過 525252 位的部分 000 舍 111 進
由於指數位的 11 位不包括符號位,那麼為瞭達到正負指數的效果,就引入瞭指數的偏移值。
用圖表示如下:
我們將轉換好的二進制數按規則放進內存中,首先 106.6953125106.6953125106.6953125 是正數,所以符號位應該為 111, 000 表示正號, 111 表示負號(圖片應該為顯示 000,筆誤瞭)
二進制 1.1010101011001∗261.1010101011001*2^61.1010101011001∗26 指數是 666(這裡需要加上偏移量1023),轉成二進制為 100000001011000000010110000000101,指數位要求放置二進制的補碼,而補碼的計算規則是:
- 正數的補碼就是其本身
- 負數的補碼是在其原碼的基礎上, 符號位不變, 其餘各位取反, 最後+1. (即在反碼的基礎上+1)
[+1] = [00000001]原 = [00000001]反 [-1] = [10000001]原 = [11111110]反
所以圖片指數位應該填
尾數位部分直接將小數轉換後的二進制填入即可
數字最後就是以這樣的形式存入計算機中
why 0.1 + 0.2 !== 0.3?
在理解數字存儲的原理後,我們再來分析下為什麼 0.1+0.2!==0.30.1 + 0.2 !== 0.30.1+0.2!==0.3
首先將 0.10.10.1 0.20.20.2 0.30.30.3 分別轉換成二進制
0.1 x 2 = 0.2 ...... 0 0.2 x 2 = 0.4 ...... 0 0.4 x 2 = 0.8 ...... 0 0.8 x 2 = 1.6 ...... 1 0.6 x 2 = 1.2 ...... 1 0.2 x 2 = 0.4 ...... 0 0.4 x 2 = 0.8 ...... 0 0.8 x 2 = 1.6 ...... 1 0.6 x 2 = 1.2 ...... 1 得到的整數位按照從左往右排列 000110011...
0.1→0.00011(0011)∞
0.2 x 2 = 0.4 ...... 0 0.4 x 2 = 0.8 ...... 0 0.8 x 2 = 1.6 ...... 1 0.6 x 2 = 1.2 ...... 1 0.2 x 2 = 0.4 ...... 0 0.4 x 2 = 0.8 ...... 0 0.8 x 2 = 1.6 ...... 1 0.6 x 2 = 1.2 ...... 1 0.2 x 2 = 0.4 ...... 0 得到的整數位按照從左往右排列 001100110...
0.2→0.00110(0110)∞
0.3 x 2 = 0.6 ...... 0 0.6 x 2 = 1.2 ...... 1 0.2 x 2 = 0.4 ...... 0 0.4 x 2 = 0.8 ...... 0 0.8 x 2 = 1.6 ...... 1 0.6 x 2 = 1.2 ...... 1 0.2 x 2 = 0.4 ...... 0 0.4 x 2 = 0.8 ...... 0 0.8 x 2 = 1.6 ...... 1 得到的整數位按照從左往右排列 010011001...
0.3→0.01001(1001)∞
統一用科學計數法表示為
0.1→0.00011(0011)∞→1.(1001)∞∗2−4
0.2→0.00110(0110)∞→1.(1001)∞∗2−3
0.3→0.01001(1001)∞→1.(0011)∞∗2−2
放入計算機中雙精度浮點數存儲,最後的紅色表示超過尾數位的二進制,即需要做舍0進1處理
則經過64位雙精度存儲後,二進制如下表示
0.1→0−01111111011−(1001)121010
0.2→0−01111111100−(1001)121010
0.3→0−01111111101−(0011)120011
此時 0.1+0.20.1 + 0.20.1+0.2 可以看出與 0.30.30.3 不相等
這就是數字在計算機中運算往往會給人帶來一些“驚喜”!
總結
到此這篇關於JS中內存與變量存儲的文章就介紹到這瞭,更多相關JS內存與變量存儲內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 詳解JSON.parse和JSON.stringify用法
- 前端進階之教你利用javascript存儲函數
- JavaScript JSON.stringify()的使用總結
- JavaScript對象與JSON格式的轉換及JSON.stringify和JSON.parse的使用方法
- JS不要再到處使用絕對等於運算符瞭