[TOC]
## 概述
### 整數和浮點數
JavaScript內部,所有數字都是以64位浮點數形式儲存,即使整數也是如此。所以,1與1.0是相等的,而且1加上1.0得到的還是一個整數,不會像有些語言那樣變成小數。
~~~
1 === 1.0 // true
1 + 1.0 // 2
~~~
由于浮點數不是精確的值,所以涉及小數的比較和運算要特別小心。
~~~
0.1 + 0.2 === 0.3
// false
0.3 / 0.1
// 2.9999999999999996
(0.3-0.2) === (0.2-0.1)
// false
~~~
### 數值精度
根據國際標準IEEE 754,64位浮點數格式的64個二進制位中,第0位到第51位儲存有效數字部分,第52到第62位儲存指數部分,第63位是符號位,0表示正數,1表示負數。
因此,JavaScript提供的有效數字的精度為53個二進制位(IEEE 754規定有效數字第一位默認為1,再加上后面的52位),也就是說,絕對值小于等于2的53次方的整數都可以精確表示。
~~~
Math.pow(2, 53) // 54個二進制位
// 9007199254740992
Math.pow(2, 53) + 1
// 9007199254740992
Math.pow(2, 53) + 2
// 9007199254740994
Math.pow(2, 53) + 3
// 9007199254740996
Math.pow(2, 53) + 4
// 9007199254740996
~~~
從上面示例可以看到,大于2的53次方以后,整數運算的結果開始出現錯誤。所以,大于等于2的53次方的數值,都無法保持精度。
~~~
Math.pow(2, 53)
// 9007199254740992
9007199254740992111
// 9007199254740992000
~~~
上面示例表明,大于2的53次方以后,多出來的有效數字(最后三位的111)都會無法保存,變成0。
### 數值范圍
另一方面,64位浮點數的指數部分的長度是11個二進制位,意味著指數部分的最大值是2047(2的11次方減1)。也就是說,64位浮點數的指數部分的值最大為2047,分出一半表示負數,則JavaScript能夠表示的數值范圍為21024到2-1023(開區間),超出這個范圍的數無法表示。
如果指數部分等于或超過最大正值1024,JavaScript會返回Infinity(關于Infinity的介紹參見下文),這稱為“正向溢出”;如果等于或超過最小負值-1023(即非常接近0),JavaScript會直接把這個數轉為0,這稱為“負向溢出”。事實上,JavaScript對指數部分的兩個極端值(11111111111和00000000000)做了定義,11111111111表示NaN和Infinity,00000000000表示0。
~~~
var x = 0.5;
for(var i =0;i<25;i++) x = x*x;
x // 0
~~~
上面代碼對0.5連續做25次平方,由于最后結果太接近0,超出了可表示的范圍,JavaScript就直接將其轉為0。
至于具體的最大值和最小值,JavaScript提供Number對象的MAX_VALUE和MIN_VALUE屬性表示(參見《Number對象》一節)。
~~~
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324
~~~
## 數值的表示法
JavaScript的數值有多種表示方法,可以用字面形式直接表示,也可以采用科學計數法表示,下面是兩個科學計數法的例子。
~~~
123e3 // 123000
123e-3 // 0.123
~~~
以下兩種情況,JavaScript會自動將數值轉為科學計數法表示,其他情況都采用字面形式直接表示。
(1)小數點前的數字多于21位。
~~~
1234567890123456789012
// 1.2345678901234568e+21
123456789012345678901
// 123456789012345680000
~~~
(2)小數點后的零多于5個。
~~~
0.0000003 // 3e-7
0.000003 // 0.000003
~~~
正常情況下,所有數值都為十進制。如果要表示十六進制的數,必須以0x或0X開頭,比如十進制的255等于十六進制的0xff或0Xff。如果要表示八進制數,必須以0開頭,比如十進制的255等于八進制的0377。由于八進制表示法的前置0,在處理時很容易造成混亂,有時為了區分一個數到底是八進制還是十進制,會增加很大的麻煩,所以建議不要使用這種表示法。
## 特殊數值
JavaScript提供幾個特殊的數值。
### 正零和負零
嚴格來說,JavaScript提供零的三種寫法:0、+0、-0。它們是等價的。
~~~
-0 === +0 // true
0 === -0 // true
0 === +0 // true
~~~
但是,如果正零和負零分別當作分母,它們返回的值是不相等的。
~~~
(1/+0) === (1/-0) // false
~~~
上面代碼之所以出現這樣結果,是因為除以正零得到+Infinity,除以負零得到-Infinity,這兩者是不相等的(關于Infinity詳見后文)。
### NaN
(1)含義
NaN是JavaScript的特殊值,表示“非數字”(Not a Number),主要出現在將字符串解析成數字出錯的場合。
~~~
5 - 'x'
// NaN
~~~
上面代碼運行時,會自動將字符串“x”轉為數值,但是由于x不是數字,所以最后得到結果為NaN,表示它是“非數字”(NaN)。
另外,一些數學函數的運算結果會出現NaN。
~~~
Math.acos(2) // NaN
Math.log(-1) // NaN
Math.sqrt(-1) // NaN
~~~
0除以0也會得到NaN。
~~~
0 / 0 // NaN
~~~
需要注意的是,NaN不是一種獨立的數據類型,而是一種特殊數值,它的數據類型依然屬于Number,使用typeof運算符可以看得很清楚。
~~~
typeof NaN // 'number'
~~~
(2)運算規則
NaN不等于任何值,包括它本身。
~~~
NaN === NaN // false
~~~
由于數組的indexOf方法,內部使用的是嚴格相等運算符,所以該方法對NaN不成立。
~~~
[NaN].indexOf(NaN) // -1
~~~
NaN在布爾運算時被當作false。
~~~
Boolean(NaN) // false
~~~
NaN與任何數(包括它自己)的運算,得到的都是NaN。
~~~
NaN + 32 // NaN
NaN - 32 // NaN
NaN * 32 // NaN
NaN / 32 // NaN
~~~
(3)判斷NaN的方法
isNaN方法可以用來判斷一個值是否為NaN。
~~~
isNaN(NaN) // true
isNaN(123) // false
~~~
但是,isNaN只對數值有效,如果傳入其他值,會被先轉成數值。比如,傳入字符串的時候,字符串會被先轉成NaN,所以最后返回true,這一點要特別引起注意。也就是說,isNaN為true的值,有可能不是NaN,而是一個字符串。
~~~
isNaN("Hello") // true
// 相當于
isNaN(Number("Hello")) // true
~~~
出于同樣的原因,對于數組和對象,isNaN也返回true。
~~~
isNaN({}) // true
isNaN(Number({})) // true
isNaN(["xzy"]) // true
isNaN(Number(["xzy"])) // true
~~~
因此,使用isNaN之前,最好判斷一下數據類型。
~~~
function myIsNaN(value) {
return typeof value === 'number' && isNaN(value);
}
~~~
判斷NaN更可靠的方法是,利用NaN是JavaScript之中唯一不等于自身的值這個特點,進行判斷。
~~~
function myIsNaN(value) {
return value !== value;
}
~~~
### Infinity
(1)定義
Infinity表示“無窮”。除了0除以0得到NaN,其他任意數除以0,得到Infinity。
~~~
1 / -0 // -Infinity
1 / +0 // Infinity
~~~
上面代碼表示,非0值除以0,JavaScript不報錯,而是返回Infinity。這是需要特別注意的地方。
Infinity有正負之分。
~~~
Infinity === -Infinity // false
Math.pow(+0, -1) // Infinity
Math.pow(-0, -1) // -Infinity
~~~
運算結果超出JavaScript可接受范圍,也會返回無窮。
~~~
Math.pow(2, 2048) // Infinity
-Math.pow(2, 2048) // -Infinity
~~~
由于數值正向溢出(overflow)、負向溢出(underflow)和被0除,JavaScript都不報錯,所以單純的數學運算幾乎沒有可能拋出錯誤。
(2)運算規則
Infinity的四則運算,符合無窮的數學計算規則。
~~~
Infinity + Infinity // Infinity
5 * Infinity // Infinity
5 - Infinity // -Infinity
Infinity / 5 // Infinity
5 / Infinity // 0
~~~
Infinity減去或除以Infinity,得到NaN。
~~~
Infinity - Infinity // NaN
Infinity / Infinity // NaN
~~~
Infinity可以用于布爾運算。可以記住,Infinity是JavaScript中最大的值(NaN除外),-Infinity是最小的值(NaN除外)。
~~~
5 > -Infinity // true
5 > Infinity // false
~~~
(3)isFinite函數
isFinite函數返回一個布爾值,檢查某個值是否為正常值,而不是Infinity。
~~~
isFinite(Infinity) // false
isFinite(-1) // true
isFinite(true) // true
isFinite(NaN) // false
~~~
上面代碼表示,如果對NaN使用isFinite函數,也返回false,表示NaN不是一個正常值。
## 與數值相關的全局方法
### parseInt方法
parseInt方法可以將字符串或小數轉化為整數。如果字符串頭部有空格,空格會被自動去除。
~~~
parseInt("123") // 123
parseInt(1.23) // 1
parseInt(' 81') // 81
~~~
如果字符串包含不能轉化為數字的字符,則不再進行轉化,返回已經轉好的部分。
~~~
parseInt("8a") // 8
parseInt("12**") // 12
parseInt("12.34") // 12
~~~
如果字符串的第一個字符不能轉化為數字(正負號除外),返回NaN。
~~~
parseInt("abc") // NaN
parseInt(".3") // NaN
parseInt("") // NaN
~~~
parseInt方法還可以接受第二個參數(2到36之間),表示被解析的值的進制。
~~~
parseInt(1000, 2) // 8
parseInt(1000, 6) // 216
parseInt(1000, 8) // 512
~~~
這意味著,可以用parseInt方法進行進制的轉換。
~~~
parseInt("1011", 2) // 11
~~~
需要注意的是,如果第一個參數是數值,則會將這個數值先轉為10進制,然后再應用第二個參數。
~~~
parseInt(010, 10) // 8
parseInt(010, 8) // NaN
parseInt(020, 10) // 16
parseInt(020, 8) // 14
~~~
上面代碼中,010會被先轉為十進制8,然后再應用第二個參數,由于八進制中沒有8這個數字,所以parseInt(010, 8)會返回NaN。同理,020會被先轉為十進制16,然后再應用第二個參數。
如果第一個參數是字符串,則不會將其先轉為十進制。
~~~
parseInt("010") // 10
parseInt("010",10) // 10
parseInt("010",8) // 8
~~~
可以看到,parseInt的很多復雜行為,都是由八進制的前綴0引發的。因此,ECMAScript 5不再允許parseInt將帶有前綴0的數字,視為八進制數。但是,為了保證兼容性,大部分瀏覽器并沒有部署這一條規定。
另外,對于那些會自動轉為科學計數法的數字,parseInt會出現一些奇怪的錯誤。
~~~
parseInt(1000000000000000000000.5, 10) // 1
// 等同于
parseInt('1e+21', 10) // 1
parseInt(0.0000008, 10) // 8
// 等同于
parseInt('8e-7', 10) // 8
~~~
### parseFloat方法
parseFloat方法用于將一個字符串轉為浮點數。
如果字符串包含不能轉化為浮點數的字符,則不再進行轉化,返回已經轉好的部分。
~~~
parseFloat("3.14");
parseFloat("314e-2");
parseFloat("0.0314E+2");
parseFloat("3.14more non-digit characters");
~~~
上面四個表達式都返回3.14。
parseFloat方法會自動過濾字符串前導的空格。
~~~
parseFloat("\t\v\r12.34\n ")
// 12.34
~~~
如果第一個字符不能轉化為浮點數,則返回NaN。
~~~
parseFloat("FF2") // NaN
parseFloat("") // NaN
~~~
上面代碼說明,parseFloat將空字符串轉為NaN。
這使得parseFloat的轉換結果不同于Number函數。
~~~
parseFloat(true) // NaN
Number(true) // 1
parseFloat(null) // NaN
Number(null) // 0
parseFloat('') // NaN
Number('') // 0
parseFloat('123.45#') // 123.45
Number('123.45#') // NaN
~~~
## 參考鏈接
* Dr. Axel Rauschmayer,?[How numbers are encoded in JavaScript](http://www.2ality.com/2012/04/number-encoding.html)
* Humphry,?[JavaScript中Number的一些表示上/下限](http://blog.segmentfault.com/humphry/1190000000407658)
- 第一章 導論
- 1.1 前言
- 1.2 為什么學習JavaScript?
- 1.3 JavaScript的歷史
- 第二章 基本語法
- 2.1 語法概述
- 2.2 數值
- 2.3 字符串
- 2.4 對象
- 2.5 數組
- 2.6 函數
- 2.7 運算符
- 2.8 數據類型轉換
- 2.9 錯誤處理機制
- 2.10 JavaScript 編程風格
- 第三章 標準庫
- 3.1 Object對象
- 3.2 Array 對象
- 3.3 包裝對象和Boolean對象
- 3.4 Number對象
- 3.5 String對象
- 3.6 Math對象
- 3.7 Date對象
- 3.8 RegExp對象
- 3.9 JSON對象
- 3.10 ArrayBuffer:類型化數組
- 第四章 面向對象編程
- 4.1 概述
- 4.2 封裝
- 4.3 繼承
- 4.4 模塊化編程
- 第五章 DOM
- 5.1 Node節點
- 5.2 document節點
- 5.3 Element對象
- 5.4 Text節點和DocumentFragment節點
- 5.5 Event對象
- 5.6 CSS操作
- 5.7 Mutation Observer
- 第六章 瀏覽器對象
- 6.1 瀏覽器的JavaScript引擎
- 6.2 定時器
- 6.3 window對象
- 6.4 history對象
- 6.5 Ajax
- 6.6 同域限制和window.postMessage方法
- 6.7 Web Storage:瀏覽器端數據儲存機制
- 6.8 IndexedDB:瀏覽器端數據庫
- 6.9 Web Notifications API
- 6.10 Performance API
- 6.11 移動設備API
- 第七章 HTML網頁的API
- 7.1 HTML網頁元素
- 7.2 Canvas API
- 7.3 SVG 圖像
- 7.4 表單
- 7.5 文件和二進制數據的操作
- 7.6 Web Worker
- 7.7 SSE:服務器發送事件
- 7.8 Page Visibility API
- 7.9 Fullscreen API:全屏操作
- 7.10 Web Speech
- 7.11 requestAnimationFrame
- 7.12 WebSocket
- 7.13 WebRTC
- 7.14 Web Components
- 第八章 開發工具
- 8.1 console對象
- 8.2 PhantomJS
- 8.3 Bower:客戶端庫管理工具
- 8.4 Grunt:任務自動管理工具
- 8.5 Gulp:任務自動管理工具
- 8.6 Browserify:瀏覽器加載Node.js模塊
- 8.7 RequireJS和AMD規范
- 8.8 Source Map
- 8.9 JavaScript 程序測試
- 第九章 JavaScript高級語法
- 9.1 Promise對象
- 9.2 有限狀態機
- 9.3 MVC框架與Backbone.js
- 9.4 嚴格模式
- 9.5 ECMAScript 6 介紹
- 附錄
- 10.1 JavaScript API列表
- 草稿一:函數庫
- 11.1 Underscore.js
- 11.2 Modernizr
- 11.3 Datejs
- 11.4 D3.js
- 11.5 設計模式
- 11.6 排序算法
- 草稿二:jQuery
- 12.1 jQuery概述
- 12.2 jQuery工具方法
- 12.3 jQuery插件開發
- 12.4 jQuery.Deferred對象
- 12.5 如何做到 jQuery-free?
- 草稿三:Node.js
- 13.1 Node.js 概述
- 13.2 CommonJS規范
- 13.3 package.json文件
- 13.4 npm模塊管理器
- 13.5 fs 模塊
- 13.6 Path模塊
- 13.7 process對象
- 13.8 Buffer對象
- 13.9 Events模塊
- 13.10 stream接口
- 13.11 Child Process模塊
- 13.12 Http模塊
- 13.13 assert 模塊
- 13.14 Cluster模塊
- 13.15 os模塊
- 13.16 Net模塊和DNS模塊
- 13.17 Express框架
- 13.18 Koa 框架