我從AngularJS
開始,閱讀開發人員指南 - 概念性概述。AngularJS官方指南中的數學問題
我只得到了第一個例子 - 簡單的計算器 - 發現AngularJS中,一個奇怪的現象,即下面的表達式:
{{數量*成本|貨幣}}
小數字它的工作原理,大數輸出結果指數,但結果之間的一些值,意外的是,NaN
。
在香草相同的數字javascript當然不返回NaN
。
例如:
2222222222 and 200000000000
是否有人可以解釋這種現象?
我從AngularJS
開始,閱讀開發人員指南 - 概念性概述。AngularJS官方指南中的數學問題
我只得到了第一個例子 - 簡單的計算器 - 發現AngularJS中,一個奇怪的現象,即下面的表達式:
{{數量*成本|貨幣}}
小數字它的工作原理,大數輸出結果指數,但結果之間的一些值,意外的是,NaN
。
在香草相同的數字javascript當然不返回NaN
。
例如:
2222222222 and 200000000000
是否有人可以解釋這種現象?
找出這些問題的最好方法是嘗試重新創建它們。開發人員工具中的控制檯非常適合這一點。
> 2222222222 * 200000000000
444444444400000000000
所以,這裏沒有什麼奇怪的。 Javascript可以處理乘法。這意味着我們可以確定古怪是來自貨幣過濾器。
下一站是過濾器本身的來源。如果你訪問Angular module的文檔,那麼有一個view source按鈕,它可以直接轉到GitHub上的源文件。
貨幣過濾器本身並不是太多的代碼。
function currencyFilter($locale) {
var formats = $locale.NUMBER_FORMATS;
return function(amount, currencySymbol, fractionSize) {
if (isUndefined(currencySymbol)) {
currencySymbol = formats.CURRENCY_SYM;
}
if (isUndefined(fractionSize)) {
fractionSize = formats.PATTERNS[1].maxFrac;
}
// if null or undefined pass it through
return (amount == null)
? amount
: formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
replace(/\u00A4/g, currencySymbol);
};
}
的amount
變量被使用的唯一地方是在formatNumber
通話,與$locale.NUMBER_FORMATS
其他的東西負載一起。
快速檢查src/ngLocale/angular-locale_en-us.js
向我們展示了我們關心的部分。
formats.PATTERNS[1] = {
"gSize": 3,
"lgSize": 3,
"maxFrac": 2,
"minFrac": 2,
"minInt": 1,
"negPre": "-\u00a4",
"negSuf": "",
"posPre": "\u00a4",
"posSuf": ""
}
其他參數只是格式化的細節。現在讓我們看看這個formatNumber
方法。這很長,所以我會盡力削減重要的一點。
早期,我們的號碼被轉換爲字符串var numStr = number + '',
。
之後,通過讀取代碼來幹運行代碼變得非常重要,因此請回到演示並在formatNumber
函數(Angular的非縮小版本中的第18580行)的開頭處輸入斷點。
如果我們跨越功能,我們可以在本地範圍內看到numStr
分配給h
。當我們走到第18627行時,h
本地變成"NaN"
。
在18617我可以看到兩個變量b
和h
似乎包含我們的價值。
b = 444444444400000000000
h = "444444444400000000000"
而在18619 b
已成爲NaN
。 h
保持不變。所以有問題的代碼行是
number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
現在我們正在某個地方。這段代碼充斥着將試圖將數字強制轉換爲字符串的事情,反之亦然。
fractionSize
作爲該函數的最後一個參數進來。正如我們從前所知,我們的區域設置號碼格式模式的maxFrac
字段。
"maxFrac": 2,
讓我們開始替換函數調用中的值,以便我們可以在控制檯中運行它。
var number = 444444444400000000000;
var fractionSize = 2;
+(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
我們可以使用這個來確認這實際上是否創建了NaN
。
現在打破它,找出原因。最裏面的表達式如下
number.toString() + 'e' + fractionSize
// "444444444400000000000e2"
該值被強制轉換回數字與+
操作者馬上。
+(number.toString() + 'e' + fractionSize)
// 4.444444444e+22
然後這個數字是四捨五入的,但它保持不變。然後它被轉換回一個字符串。
Math.round(+(number.toString() + 'e' + fractionSize)).toString()
// "4.444444444e+22"
爲了能夠閱讀這個,讓我們打電話給那個值x
。
x = "4.444444444e+22"
現在我們串接x
與另一'e'
和-fractionSize
。
x + 'e' + -fractionSize
// "4.444444444e+22e-2"
我們開始看到哪裏出錯了。最後,再次使用+
運算符將該字符串強制爲一個數字。
+(x + 'e' + -fractionSize)
// NaN
賓果。
發生了什麼事?數字舍入的實施似乎有一個邊緣情況。在此之前,它會檢查數字是否包含指數,然後相應地處理它,如果是。但是,如圖所示,在這種舍入方法中,有一些邊緣情況下大數字最終有兩個指數。認爲這不是有效的表示法,該數字不能被強制並以NaN結尾。
這個邊緣案例幾乎可以肯定地與你選擇的數字相乘時,超過了Number.MAX_SAFE_INTEGER
(9007199254740991)的範圍。一般來說,當你處理數字這麼大的時候,你可能會發現很多代碼不能以你期望的方式處理它們。特別是如果該代碼將在字符串和數字之間來回轉換。
最後,該NaN值被轉換回一個字符串並饋送到貨幣過濾器。
建議您創建一個複製這些行爲的演示 – charlietfl
http://plnkr.co/edit/s2cu4EzLBiBJ37cyxUs9?p=preview(鏈接來自官方文檔 - https://docs.angularjs.org/tutorial/step_02) – MartinVis