理解 JavaScript 的記憶體與變數儲存
在前端領域,因為大部分在跟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。1010101011001261。1010101011001*2^61。101010101100126,算出了二進位制,接下來需要將它存進計算機中,在Javascript中不區分整數和小數,數字統一按照雙精度浮點數的要求來儲存,主要包含下面規則:
使用 8bytes(64bits)8bytes(64bits)8bytes(64bits) 儲存雙精度浮點數
儲存小數用科學計數法表示的資料
第一位表示符號,後 111111 位表示指數,
指數按照補位運算
,即直接 102310231023 加指數位
剩餘 525252 位表示小數點後的尾數,
超過 525252 位的部分 000 舍 111 進
由於指數位的 11 位不包括符號位,那麼為了達到正負指數的效果,就引入了
指數的偏移值
。
用圖表示如下:
Screen Shot 2021-08-17 at 12。20。14 PM。png
我們將轉換好的二進位制數按規則放進記憶體中,首先 106。6953125106。6953125106。6953125 是正數,所以符號位應該為 000, 000 表示正號, 111 表示負號
Screen Shot 2021-08-17 at 12。20。22 PM。png
二進位制 1。1010101011001261。1010101011001*2^61。101010101100126 指數是 666(這裡需要加上偏移量1023),轉成二進位制為 100000001011000000010110000000101,指數位要求放置二進位制的補碼,而補碼的計算規則是:
正數的補碼就是其本身
負數的補碼是在其原碼的基礎上, 符號位不變, 其餘各位取反, 最後+1。 (即在反碼的基礎上+1)
[+
1
] = [
00000001
]原 = [
00000001
]反
[
-1
] = [
10000001
]原 = [
11111110
]反
複製程式碼
所以圖片指數位應該填
Screen Shot 2021-08-17 at 12。20。47 PM。png
尾數位部分直接將小數轉換後的二進位制填入即可
Screen Shot 2021-08-17 at 12。21。37 PM。png
數字最後就是以這樣的形式存入計算機中
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。1 \rightarrow 0。00011(0011)_\infty0。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。2 \rightarrow 0。00110(0110)_\infty0。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。3 \rightarrow 0。01001(1001)_\infty0。3→0。01001(1001)∞
統一用科學計數法表示為
0。1→0。00011(0011)∞→1。(1001)∞240。1 \rightarrow 0。00011(0011)_\infty \rightarrow 1。(1001)_\infty*2^{-4}0。1→0。00011(0011)∞→1。(1001)∞24
0。2→0。00110(0110)∞→1。(1001)∞230。2 \rightarrow 0。00110(0110)_\infty \rightarrow 1。(1001)_\infty*2^{-3}0。2→0。00110(0110)∞→1。(1001)∞23
0。3→0。01001(1001)∞→1。(0011)∞220。3 \rightarrow 0。01001(1001)_\infty \rightarrow 1。(0011)_\infty*2^{-2}0。3→0。01001(1001)∞→1。(0011)∞22
放入計算機中雙精度浮點數儲存,最後的紅色表示超過尾數位的二進位制,即需要做舍0進1處理
Screen Shot 2021-08-17 at 12。30。01 PM。png
Screen Shot 2021-08-17 at 12。30。27 PM。png
Screen Shot 2021-08-17 at 12。32。27 PM。png
則經過64位雙精度儲存後,二進位制如下表示
0。1→001111111011(1001)1210100。1 \rightarrow 0-01111111011-(1001)_{12}10100。1→001111111011(1001)121010
0。2→001111111100(1001)1210100。2 \rightarrow 0-01111111100-(1001)_{12}10100。2→001111111100(1001)121010
0。3→001111111101(0011)1200110。3 \rightarrow 0-01111111101-(0011)_{12}00110。3→001111111101(0011)120011
此時 0。1+0。20。1 + 0。20。1+0。2 可以看出與 0。30。30。3 不相等
8。png
這就是數字在計算機中運算往往會給人帶來一些“驚喜”!
結束語
如果該文對您有幫助的話,請點個贊哦
文中若有錯誤,歡迎指正;若您有補充,歡迎留言。
關於本文
作者:立花正仁
https://juejin。cn/po
st/6991844682066034718
- END -
寫在最後
我是
懸筆e絕
,如果我的文章對你有幫助,請點個
贊
支援我一下
若有什麼疑問,也可以加我微信:
yllg_xbyj
,一起交流心得
看完三件事
如果你覺得這篇內容對你挺有啟發,我想邀請你幫我三個小忙:
點個「
在看
」,讓更多的人也能看到這篇內容(喜歡不點在看,都是耍流氓 -_-)
關注公眾號「
懸筆e絕
」,持續為你推送精選好文,
回覆「
加群
」加入交流群
點一下,程式碼無 Bug