2015-08-30 62 views
1

我從AngularJS開始,閱讀開發人員指南 - 概念性概述。AngularJS官方指南中的數學問題

我只得到了第一個例子 - 簡單的計算器 - 發現AngularJS中,一個奇怪的現象,即下面的表達式:

{{數量*成本|貨幣}}

小數字它的工作原理,大數輸出結果指數,但結果之間的一些值,意外的是,NaN

在香草相同的數字javascript當然不返回NaN

例如:

2222222222 and 200000000000 

是否有人可以解釋這種現象?

+1

建議您創建一個複製這些行爲的演示 – charlietfl

+0

http://plnkr.co/edit/s2cu4EzLBiBJ37cyxUs9?p=preview(鏈接來自官方文檔 - https://docs.angularjs.org/tutorial/step_02) – MartinVis

回答

1

找出這些問題的最好方法是嘗試重新創建它們。開發人員工具中的控制檯非常適合這一點。

> 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我可以看到兩個變量bh似乎包含我們的價值。

b = 444444444400000000000 
h = "444444444400000000000" 

而在18619 b已成爲NaNh保持不變。所以有問題的代碼行是

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值被轉換回一個字符串並饋送到貨幣過濾器。