JavaScript|第13章:原始类型的方法
原始类型的方法
- JavaScript|第13章:原始类型的方法
-
- 引言
- [13.1 原始类型为什么能有方法?](#13.1 原始类型为什么能有方法?)
-
- [13.1.1 原始类型 vs 对象类型](#13.1.1 原始类型 vs 对象类型)
- [13.1.2 装箱机制](#13.1.2 装箱机制)
- [13.1.3 装箱的临时性](#13.1.3 装箱的临时性)
- [13.1.4 null 和 undefined 没有方法](#13.1.4 null 和 undefined 没有方法)
- [13.1.5 原始类型与包装对象的区别](#13.1.5 原始类型与包装对象的区别)
- [13.2 数字类型的方法](#13.2 数字类型的方法)
-
- [13.2.1 数字字面量调用方法的坑](#13.2.1 数字字面量调用方法的坑)
- [13.2.2 toFixed --- 格式化小数](#13.2.2 toFixed — 格式化小数)
- [13.2.3 toString --- 进制转换](#13.2.3 toString — 进制转换)
- [13.2.4 toPrecision --- 有效数字](#13.2.4 toPrecision — 有效数字)
- [13.2.5 Number 构造函数的静态方法](#13.2.5 Number 构造函数的静态方法)
- [13.2.6 全局方法 vs Number 静态方法](#13.2.6 全局方法 vs Number 静态方法)
- [13.2.7 parseInt 与 parseFloat](#13.2.7 parseInt 与 parseFloat)
- [13.2.8 Number() vs parseInt() vs parseFloat()](#13.2.8 Number() vs parseInt() vs parseFloat())
- [13.2.9 数字的特殊值](#13.2.9 数字的特殊值)
- [13.3 精度问题与 BigInt](#13.3 精度问题与 BigInt)
-
- [13.3.1 IEEE 754 浮点数](#13.3.1 IEEE 754 浮点数)
- [13.3.2 经典精度问题:0.1 + 0.2 !== 0.3](#13.3.2 经典精度问题:0.1 + 0.2 !== 0.3)
- [13.3.3 Number.EPSILON](#13.3.3 Number.EPSILON)
- [13.3.4 数字的安全范围](#13.3.4 数字的安全范围)
- [13.3.5 BigInt --- 大整数类型](#13.3.5 BigInt — 大整数类型)
- [13.3.6 Number 与 BigInt 的互转](#13.3.6 Number 与 BigInt 的互转)
- [13.4 字符串与布尔的装箱方法](#13.4 字符串与布尔的装箱方法)
-
- [13.4.1 字符串的装箱方法](#13.4.1 字符串的装箱方法)
- [13.4.2 布尔的装箱方法](#13.4.2 布尔的装箱方法)
- [13.4.3 Symbol 的装箱](#13.4.3 Symbol 的装箱)
- [13.5 实战](#13.5 实战)
-
- [13.5.1 实战1:金额格式化](#13.5.1 实战1:金额格式化)
- [13.5.2 实战2:精度安全计算](#13.5.2 实战2:精度安全计算)
- [13.5.3 实战3:进制转换工具](#13.5.3 实战3:进制转换工具)
- [13.5.4 实战4:大数计算器](#13.5.4 实战4:大数计算器)
- [13.5.5 实战5:数字类型检测与安全转换](#13.5.5 实战5:数字类型检测与安全转换)
- [13.6 原始类型方法速查表](#13.6 原始类型方法速查表)
-
- [13.6.1 数字方法速查](#13.6.1 数字方法速查)
- [13.6.2 Number 静态常量速查](#13.6.2 Number 静态常量速查)
- [13.6.3 转换方法对比](#13.6.3 转换方法对比)
- 本章小结
上篇回顾 :第12章:属性配置与 getter/setter
下篇预告 :第14章:字符串
引言
在前面的章节中,我们学习了对象、属性描述符、getter/setter。你可能会产生一个疑问:
原始类型(
number、string、boolean)明明不是对象,为什么能调用.toFixed()、.toUpperCase()这样的方法?
javascript
const num = 3.14159;
console.log(num.toFixed(2)); // "3.14" ← number 不是对象,却能调用方法!
const str = 'hello';
console.log(str.toUpperCase()); // "HELLO" ← string 不是对象,却能调用方法!
const bool = true;
console.log(bool.toString()); // "true" ← boolean 不是对象,却能调用方法!
这背后的机制称为装箱(Boxing)------JavaScript 引擎在原始类型调用方法时,临时将其"包装"成对应的对象类型,方法执行完毕后立即"拆箱"销毁。
#mermaid-svg-Xrprr8GVbRIVDykK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Xrprr8GVbRIVDykK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Xrprr8GVbRIVDykK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Xrprr8GVbRIVDykK .error-icon{fill:#552222;}#mermaid-svg-Xrprr8GVbRIVDykK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Xrprr8GVbRIVDykK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Xrprr8GVbRIVDykK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Xrprr8GVbRIVDykK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Xrprr8GVbRIVDykK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Xrprr8GVbRIVDykK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Xrprr8GVbRIVDykK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Xrprr8GVbRIVDykK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Xrprr8GVbRIVDykK .marker.cross{stroke:#333333;}#mermaid-svg-Xrprr8GVbRIVDykK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Xrprr8GVbRIVDykK p{margin:0;}#mermaid-svg-Xrprr8GVbRIVDykK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Xrprr8GVbRIVDykK .cluster-label text{fill:#333;}#mermaid-svg-Xrprr8GVbRIVDykK .cluster-label span{color:#333;}#mermaid-svg-Xrprr8GVbRIVDykK .cluster-label span p{background-color:transparent;}#mermaid-svg-Xrprr8GVbRIVDykK .label text,#mermaid-svg-Xrprr8GVbRIVDykK span{fill:#333;color:#333;}#mermaid-svg-Xrprr8GVbRIVDykK .node rect,#mermaid-svg-Xrprr8GVbRIVDykK .node circle,#mermaid-svg-Xrprr8GVbRIVDykK .node ellipse,#mermaid-svg-Xrprr8GVbRIVDykK .node polygon,#mermaid-svg-Xrprr8GVbRIVDykK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Xrprr8GVbRIVDykK .rough-node .label text,#mermaid-svg-Xrprr8GVbRIVDykK .node .label text,#mermaid-svg-Xrprr8GVbRIVDykK .image-shape .label,#mermaid-svg-Xrprr8GVbRIVDykK .icon-shape .label{text-anchor:middle;}#mermaid-svg-Xrprr8GVbRIVDykK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Xrprr8GVbRIVDykK .rough-node .label,#mermaid-svg-Xrprr8GVbRIVDykK .node .label,#mermaid-svg-Xrprr8GVbRIVDykK .image-shape .label,#mermaid-svg-Xrprr8GVbRIVDykK .icon-shape .label{text-align:center;}#mermaid-svg-Xrprr8GVbRIVDykK .node.clickable{cursor:pointer;}#mermaid-svg-Xrprr8GVbRIVDykK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Xrprr8GVbRIVDykK .arrowheadPath{fill:#333333;}#mermaid-svg-Xrprr8GVbRIVDykK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Xrprr8GVbRIVDykK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Xrprr8GVbRIVDykK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xrprr8GVbRIVDykK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Xrprr8GVbRIVDykK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xrprr8GVbRIVDykK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Xrprr8GVbRIVDykK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Xrprr8GVbRIVDykK .cluster text{fill:#333;}#mermaid-svg-Xrprr8GVbRIVDykK .cluster span{color:#333;}#mermaid-svg-Xrprr8GVbRIVDykK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Xrprr8GVbRIVDykK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Xrprr8GVbRIVDykK rect.text{fill:none;stroke-width:0;}#mermaid-svg-Xrprr8GVbRIVDykK .icon-shape,#mermaid-svg-Xrprr8GVbRIVDykK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xrprr8GVbRIVDykK .icon-shape p,#mermaid-svg-Xrprr8GVbRIVDykK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Xrprr8GVbRIVDykK .icon-shape .label rect,#mermaid-svg-Xrprr8GVbRIVDykK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xrprr8GVbRIVDykK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Xrprr8GVbRIVDykK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Xrprr8GVbRIVDykK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始类型的方法
装箱与拆箱机制
数字类型方法
精度问题与 BigInt
实战应用
number → Number 对象
string → String 对象
boolean → Boolean 对象
toFixed / toString
parseInt / parseFloat
Number 静态方法
IEEE 754 浮点数
0.1 + 0.2 !== 0.3
BigInt 大整数
金额格式化
精度安全计算
本章目标:
- 理解原始类型调用方法的底层原理------装箱与拆箱
- 掌握数字类型的常用方法:
toFixed、toString、toPrecision - 掌握
parseInt、parseFloat与Number()的区别与选择 - 理解 IEEE 754 浮点数精度问题的根源
- 掌握
BigInt的使用场景与限制 - 实战:金额格式化、精度安全计算
13.1 原始类型为什么能有方法?
13.1.1 原始类型 vs 对象类型
JavaScript 中有 7 种原始类型:number、string、boolean、null、undefined、symbol、bigint。它们是轻量、不可变的值,存储在栈内存中。
| 特性 | 原始类型 | 对象类型 |
|---|---|---|
| 存储位置 | 栈内存(值直接存储) | 堆内存(引用存储) |
| 可变性 | 不可变 | 可变 |
| 能否有属性 | ❌ 不能 | ✅ 能 |
| 能否调用方法 | ✅ 能(通过装箱) | ✅ 能 |
| 比较 | 值比较 | 引用比较 |
javascript
// 原始类型不可变
let str = 'hello';
str.foo = 'bar'; // 尝试添加属性
console.log(str.foo); // undefined(添加失败)
// 对象类型可变
const obj = {};
obj.foo = 'bar'; // 添加属性
console.log(obj.foo); // "bar"
关键问题 :既然原始类型不能有属性,那
str.toUpperCase()为什么能正常工作?答案就是------装箱。
13.1.2 装箱机制
当原始类型值被访问(调用方法或访问属性)时,JavaScript 引擎会自动执行以下步骤:
- 检测到属性访问 :引擎发现
str.toUpperCase()中str是原始类型 - 创建临时包装对象 :引擎创建一个
String对象:new String(str) - 在包装对象上执行操作 :调用包装对象的
toUpperCase方法 - 销毁临时对象:操作完成后,包装对象立即被垃圾回收
#mermaid-svg-vWIWYYs5nEhdSaNJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vWIWYYs5nEhdSaNJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vWIWYYs5nEhdSaNJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vWIWYYs5nEhdSaNJ .error-icon{fill:#552222;}#mermaid-svg-vWIWYYs5nEhdSaNJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vWIWYYs5nEhdSaNJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vWIWYYs5nEhdSaNJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vWIWYYs5nEhdSaNJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vWIWYYs5nEhdSaNJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vWIWYYs5nEhdSaNJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vWIWYYs5nEhdSaNJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vWIWYYs5nEhdSaNJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vWIWYYs5nEhdSaNJ .marker.cross{stroke:#333333;}#mermaid-svg-vWIWYYs5nEhdSaNJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vWIWYYs5nEhdSaNJ p{margin:0;}#mermaid-svg-vWIWYYs5nEhdSaNJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vWIWYYs5nEhdSaNJ .cluster-label text{fill:#333;}#mermaid-svg-vWIWYYs5nEhdSaNJ .cluster-label span{color:#333;}#mermaid-svg-vWIWYYs5nEhdSaNJ .cluster-label span p{background-color:transparent;}#mermaid-svg-vWIWYYs5nEhdSaNJ .label text,#mermaid-svg-vWIWYYs5nEhdSaNJ span{fill:#333;color:#333;}#mermaid-svg-vWIWYYs5nEhdSaNJ .node rect,#mermaid-svg-vWIWYYs5nEhdSaNJ .node circle,#mermaid-svg-vWIWYYs5nEhdSaNJ .node ellipse,#mermaid-svg-vWIWYYs5nEhdSaNJ .node polygon,#mermaid-svg-vWIWYYs5nEhdSaNJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vWIWYYs5nEhdSaNJ .rough-node .label text,#mermaid-svg-vWIWYYs5nEhdSaNJ .node .label text,#mermaid-svg-vWIWYYs5nEhdSaNJ .image-shape .label,#mermaid-svg-vWIWYYs5nEhdSaNJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-vWIWYYs5nEhdSaNJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vWIWYYs5nEhdSaNJ .rough-node .label,#mermaid-svg-vWIWYYs5nEhdSaNJ .node .label,#mermaid-svg-vWIWYYs5nEhdSaNJ .image-shape .label,#mermaid-svg-vWIWYYs5nEhdSaNJ .icon-shape .label{text-align:center;}#mermaid-svg-vWIWYYs5nEhdSaNJ .node.clickable{cursor:pointer;}#mermaid-svg-vWIWYYs5nEhdSaNJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vWIWYYs5nEhdSaNJ .arrowheadPath{fill:#333333;}#mermaid-svg-vWIWYYs5nEhdSaNJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vWIWYYs5nEhdSaNJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vWIWYYs5nEhdSaNJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vWIWYYs5nEhdSaNJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vWIWYYs5nEhdSaNJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vWIWYYs5nEhdSaNJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vWIWYYs5nEhdSaNJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vWIWYYs5nEhdSaNJ .cluster text{fill:#333;}#mermaid-svg-vWIWYYs5nEhdSaNJ .cluster span{color:#333;}#mermaid-svg-vWIWYYs5nEhdSaNJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vWIWYYs5nEhdSaNJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vWIWYYs5nEhdSaNJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-vWIWYYs5nEhdSaNJ .icon-shape,#mermaid-svg-vWIWYYs5nEhdSaNJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vWIWYYs5nEhdSaNJ .icon-shape p,#mermaid-svg-vWIWYYs5nEhdSaNJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vWIWYYs5nEhdSaNJ .icon-shape .label rect,#mermaid-svg-vWIWYYs5nEhdSaNJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vWIWYYs5nEhdSaNJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vWIWYYs5nEhdSaNJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vWIWYYs5nEhdSaNJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} str.toUpperCase()
引擎检测到 str 是原始类型
创建临时对象 new String(str)
调用临时对象.toUpperCase()
返回 'HELLO'
销毁临时对象
str 仍然是原始类型 'hello'
我们可以手动模拟这个过程:
javascript
const str = 'hello';
// 引擎在背后做了类似这样的事情:
const tempObj = new String(str); // 装箱:创建 String 对象
const result = tempObj.toUpperCase(); // 在对象上调用方法
tempObj = null; // 拆箱:销毁临时对象(实际由 GC 回收)
console.log(result); // "HELLO"
console.log(typeof str); // "string"(str 仍然是原始类型)
13.1.3 装箱的临时性
临时包装对象在表达式执行完毕后立即销毁,因此给它添加属性不会有任何效果:
javascript
const str = 'hello';
// 每次访问都会创建新的临时对象
str.foo = 'bar'; // 装箱 → 添加属性 → 销毁
console.log(str.foo); // 再次装箱(新对象,没有 foo)→ undefined
// 等价于:
(new String('hello')).foo = 'bar'; // 在临时对象上添加属性
(new String('hello')).foo; // 创建了另一个新对象,没有 foo → undefined
#mermaid-svg-5KHjiHixkOZjLhey{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5KHjiHixkOZjLhey .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5KHjiHixkOZjLhey .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5KHjiHixkOZjLhey .error-icon{fill:#552222;}#mermaid-svg-5KHjiHixkOZjLhey .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5KHjiHixkOZjLhey .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5KHjiHixkOZjLhey .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5KHjiHixkOZjLhey .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5KHjiHixkOZjLhey .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5KHjiHixkOZjLhey .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5KHjiHixkOZjLhey .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5KHjiHixkOZjLhey .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5KHjiHixkOZjLhey .marker.cross{stroke:#333333;}#mermaid-svg-5KHjiHixkOZjLhey svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5KHjiHixkOZjLhey p{margin:0;}#mermaid-svg-5KHjiHixkOZjLhey .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5KHjiHixkOZjLhey .cluster-label text{fill:#333;}#mermaid-svg-5KHjiHixkOZjLhey .cluster-label span{color:#333;}#mermaid-svg-5KHjiHixkOZjLhey .cluster-label span p{background-color:transparent;}#mermaid-svg-5KHjiHixkOZjLhey .label text,#mermaid-svg-5KHjiHixkOZjLhey span{fill:#333;color:#333;}#mermaid-svg-5KHjiHixkOZjLhey .node rect,#mermaid-svg-5KHjiHixkOZjLhey .node circle,#mermaid-svg-5KHjiHixkOZjLhey .node ellipse,#mermaid-svg-5KHjiHixkOZjLhey .node polygon,#mermaid-svg-5KHjiHixkOZjLhey .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5KHjiHixkOZjLhey .rough-node .label text,#mermaid-svg-5KHjiHixkOZjLhey .node .label text,#mermaid-svg-5KHjiHixkOZjLhey .image-shape .label,#mermaid-svg-5KHjiHixkOZjLhey .icon-shape .label{text-anchor:middle;}#mermaid-svg-5KHjiHixkOZjLhey .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5KHjiHixkOZjLhey .rough-node .label,#mermaid-svg-5KHjiHixkOZjLhey .node .label,#mermaid-svg-5KHjiHixkOZjLhey .image-shape .label,#mermaid-svg-5KHjiHixkOZjLhey .icon-shape .label{text-align:center;}#mermaid-svg-5KHjiHixkOZjLhey .node.clickable{cursor:pointer;}#mermaid-svg-5KHjiHixkOZjLhey .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5KHjiHixkOZjLhey .arrowheadPath{fill:#333333;}#mermaid-svg-5KHjiHixkOZjLhey .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5KHjiHixkOZjLhey .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5KHjiHixkOZjLhey .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5KHjiHixkOZjLhey .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5KHjiHixkOZjLhey .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5KHjiHixkOZjLhey .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5KHjiHixkOZjLhey .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5KHjiHixkOZjLhey .cluster text{fill:#333;}#mermaid-svg-5KHjiHixkOZjLhey .cluster span{color:#333;}#mermaid-svg-5KHjiHixkOZjLhey div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5KHjiHixkOZjLhey .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5KHjiHixkOZjLhey rect.text{fill:none;stroke-width:0;}#mermaid-svg-5KHjiHixkOZjLhey .icon-shape,#mermaid-svg-5KHjiHixkOZjLhey .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5KHjiHixkOZjLhey .icon-shape p,#mermaid-svg-5KHjiHixkOZjLhey .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5KHjiHixkOZjLhey .icon-shape .label rect,#mermaid-svg-5KHjiHixkOZjLhey .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5KHjiHixkOZjLhey .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5KHjiHixkOZjLhey .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5KHjiHixkOZjLhey :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} str.foo = 'bar'
装箱:new String('hello')
设置 .foo = 'bar'
销毁临时对象
str.foo 读取
再次装箱:new String('hello')
新对象没有 foo 属性
返回 undefined
核心要点 :装箱是临时的、隐式的。每次属性访问都会创建一个新的包装对象,访问结束后立即销毁。
13.1.4 null 和 undefined 没有方法
并非所有原始类型都有对应的包装对象。null 和 undefined 是两个特殊的原始类型,它们没有包装对象,因此调用方法会直接报错:
javascript
// null 和 undefined 没有方法
// null.toString(); // TypeError: Cannot read properties of null
// undefined.toString(); // TypeError: Cannot read properties of undefined
// 访问属性也会报错
// null.foo; // TypeError
// undefined.foo; // TypeError
| 原始类型 | 包装对象 | 能否调用方法 |
|---|---|---|
number |
Number |
✅ |
string |
String |
✅ |
boolean |
Boolean |
✅ |
symbol |
Symbol |
✅ |
bigint |
BigInt |
✅ |
null |
无 | ❌ |
undefined |
无 | ❌ |
最佳实践 :在访问可能为
null或undefined的值的方法时,使用可选链?.避免报错(回顾第 11 章的内容)。
13.1.5 原始类型与包装对象的区别
虽然 new Number(42) 和 42 都能调用数字方法,但它们有本质区别:
javascript
const primitive = 42; // 原始类型
const wrapped = new Number(42); // 包装对象
console.log(typeof primitive); // "number"
console.log(typeof wrapped); // "object"
console.log(primitive === 42); // true
console.log(wrapped === 42); // false(对象不等于原始值)
console.log(wrapped == 42); // true(== 会触发拆箱转换)
// 包装对象可以添加属性(但不建议这样做)
wrapped.custom = 'hello';
console.log(wrapped.custom); // "hello"
| 特性 | 原始类型 42 |
包装对象 new Number(42) |
|---|---|---|
typeof |
"number" |
"object" |
=== 42 |
true |
false |
| 可添加属性 | ❌ | ✅ |
| 推荐使用 | ✅ | ❌ |
强烈建议 :永远不要手动使用
new Number()、new String()、new Boolean()创建包装对象。它们只会带来混乱和 Bug。
13.2 数字类型的方法
13.2.1 数字字面量调用方法的坑
数字字面量直接调用方法时,小数点会被误解为小数点而非成员访问符:
javascript
// ❌ 错误:引擎将 5 .toFixed 解析为 5.toFixed → 语法错误
// 5.toFixed(2); // SyntaxError
// ✅ 正确写法1:用两个点(第一个是小数点,第二个是成员访问)
console.log(5..toFixed(2)); // "5.00"
// ✅ 正确写法2:用括号包裹数字
console.log((5).toFixed(2)); // "5.00"
// ✅ 正确写法3:变量调用
const num = 5;
console.log(num.toFixed(2)); // "5.00"
#mermaid-svg-8cr0rJZ4mQkGm6No{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8cr0rJZ4mQkGm6No .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8cr0rJZ4mQkGm6No .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8cr0rJZ4mQkGm6No .error-icon{fill:#552222;}#mermaid-svg-8cr0rJZ4mQkGm6No .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8cr0rJZ4mQkGm6No .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8cr0rJZ4mQkGm6No .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8cr0rJZ4mQkGm6No .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8cr0rJZ4mQkGm6No .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8cr0rJZ4mQkGm6No .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8cr0rJZ4mQkGm6No .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8cr0rJZ4mQkGm6No .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8cr0rJZ4mQkGm6No .marker.cross{stroke:#333333;}#mermaid-svg-8cr0rJZ4mQkGm6No svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8cr0rJZ4mQkGm6No p{margin:0;}#mermaid-svg-8cr0rJZ4mQkGm6No .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8cr0rJZ4mQkGm6No .cluster-label text{fill:#333;}#mermaid-svg-8cr0rJZ4mQkGm6No .cluster-label span{color:#333;}#mermaid-svg-8cr0rJZ4mQkGm6No .cluster-label span p{background-color:transparent;}#mermaid-svg-8cr0rJZ4mQkGm6No .label text,#mermaid-svg-8cr0rJZ4mQkGm6No span{fill:#333;color:#333;}#mermaid-svg-8cr0rJZ4mQkGm6No .node rect,#mermaid-svg-8cr0rJZ4mQkGm6No .node circle,#mermaid-svg-8cr0rJZ4mQkGm6No .node ellipse,#mermaid-svg-8cr0rJZ4mQkGm6No .node polygon,#mermaid-svg-8cr0rJZ4mQkGm6No .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8cr0rJZ4mQkGm6No .rough-node .label text,#mermaid-svg-8cr0rJZ4mQkGm6No .node .label text,#mermaid-svg-8cr0rJZ4mQkGm6No .image-shape .label,#mermaid-svg-8cr0rJZ4mQkGm6No .icon-shape .label{text-anchor:middle;}#mermaid-svg-8cr0rJZ4mQkGm6No .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8cr0rJZ4mQkGm6No .rough-node .label,#mermaid-svg-8cr0rJZ4mQkGm6No .node .label,#mermaid-svg-8cr0rJZ4mQkGm6No .image-shape .label,#mermaid-svg-8cr0rJZ4mQkGm6No .icon-shape .label{text-align:center;}#mermaid-svg-8cr0rJZ4mQkGm6No .node.clickable{cursor:pointer;}#mermaid-svg-8cr0rJZ4mQkGm6No .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8cr0rJZ4mQkGm6No .arrowheadPath{fill:#333333;}#mermaid-svg-8cr0rJZ4mQkGm6No .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8cr0rJZ4mQkGm6No .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8cr0rJZ4mQkGm6No .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8cr0rJZ4mQkGm6No .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8cr0rJZ4mQkGm6No .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8cr0rJZ4mQkGm6No .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8cr0rJZ4mQkGm6No .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8cr0rJZ4mQkGm6No .cluster text{fill:#333;}#mermaid-svg-8cr0rJZ4mQkGm6No .cluster span{color:#333;}#mermaid-svg-8cr0rJZ4mQkGm6No div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8cr0rJZ4mQkGm6No .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8cr0rJZ4mQkGm6No rect.text{fill:none;stroke-width:0;}#mermaid-svg-8cr0rJZ4mQkGm6No .icon-shape,#mermaid-svg-8cr0rJZ4mQkGm6No .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8cr0rJZ4mQkGm6No .icon-shape p,#mermaid-svg-8cr0rJZ4mQkGm6No .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8cr0rJZ4mQkGm6No .icon-shape .label rect,#mermaid-svg-8cr0rJZ4mQkGm6No .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8cr0rJZ4mQkGm6No .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8cr0rJZ4mQkGm6No .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8cr0rJZ4mQkGm6No :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 5.toFixed(2)
引擎解析
5. 被解析为浮点数 5.0
toFixed 变成标识符
语法错误:5.0 toFixed
5..toFixed(2)
第一个 . 是小数点
第二个 . 是成员访问
正确:(5.0).toFixed(2)
最佳实践:使用变量或括号调用方法,避免直接在字面量上调用。
13.2.2 toFixed --- 格式化小数
toFixed(digits) 将数字转换为字符串,保留指定小数位数,四舍五入。
javascript
const num = 3.14159;
console.log(num.toFixed(0)); // "3"(0 位小数)
console.log(num.toFixed(1)); // "3.1"(1 位小数)
console.log(num.toFixed(2)); // "3.14"(2 位小数)
console.log(num.toFixed(4)); // "3.1416"(4 位小数,四舍五入)
// 注意:返回的是字符串,不是数字
console.log(typeof num.toFixed(2)); // "string"
// 转回数字可以用 Number() 或 +
console.log(Number(num.toFixed(2))); // 3.14
console.log(+num.toFixed(2)); // 3.14
toFixed 的四舍五入陷阱:
toFixed 使用的是"银行家舍入法"的变体,并非标准的四舍五入。由于浮点数精度问题,某些值的行为可能出乎意料:
javascript
// 正常的四舍五入
console.log((1.005).toFixed(2)); // "1.00" ← 期望 "1.01",但得到 "1.00"
console.log((1.015).toFixed(2)); // "1.02" ← 正确
console.log((1.025).toFixed(2)); // "1.02" ← 期望 "1.03",但得到 "1.02"
console.log((1.035).toFixed(2)); // "1.04" ← 正确
console.log((0.5).toFixed(0)); // "0" ← 期望 "1",但得到 "0"(某些引擎)
console.log((1.5).toFixed(0)); // "2" ← 正确
原因 :
1.005在 IEEE 754 中的实际存储值约为1.0049999999999999...,toFixed基于这个实际值进行舍入,因此结果为"1.00"。
解决 toFixed 精度问题的方案:
javascript
/**
* 精确的四舍五入
* @param {number} num - 要舍入的数字
* @param {number} digits - 保留小数位数
* @returns {string} 格式化后的字符串
*/
function toFixedPrecise(num, digits) {
// 使用 Number.EPSILON 修正精度问题
const factor = Math.pow(10, digits);
const corrected = Math.round((num + Number.EPSILON) * factor) / factor;
return corrected.toFixed(digits);
}
console.log(toFixedPrecise(1.005, 2)); // "1.01" ✅
console.log(toFixedPrecise(1.025, 2)); // "1.03" ✅
console.log(toFixedPrecise(0.5, 0)); // "1" ✅
13.2.3 toString --- 进制转换
toString(radix) 将数字转换为指定进制的字符串表示。
javascript
const num = 255;
// 默认十进制
console.log(num.toString()); // "255"
// 转换为其他进制
console.log(num.toString(2)); // "11111111"(二进制)
console.log(num.toString(8)); // "377"(八进制)
console.log(num.toString(16)); // "ff"(十六进制)
console.log(num.toString(36)); // "73"(三十六进制,最高支持)
// 常用场景:生成随机十六进制颜色
function randomColor() {
const r = Math.floor(Math.random() * 256).toString(16).padStart(2, '0');
const g = Math.floor(Math.random() * 256).toString(16).padStart(2, '0');
const b = Math.floor(Math.random() * 256).toString(16).padStart(2, '0');
return `#${r}${g}${b}`;
}
console.log(randomColor()); // 例如 "#a3f5b1"
反向操作:从其他进制转回十进制:
javascript
// parseInt(str, radix) 将字符串按指定进制解析为十进制数字
console.log(parseInt('11111111', 2)); // 255
console.log(parseInt('ff', 16)); // 255
console.log(parseInt('377', 8)); // 255
// ES2015+ 可以用 0b、0o、0x 前缀直接写进制字面量
console.log(0b11111111); // 255(二进制)
console.log(0o377); // 255(八进制)
console.log(0xff); // 255(十六进制)
| 方法 | 方向 | 示例 |
|---|---|---|
num.toString(radix) |
十进制 → 其他进制 | (255).toString(16) → "ff" |
parseInt(str, radix) |
其他进制 → 十进制 | parseInt('ff', 16) → 255 |
0b / 0o / 0x 前缀 |
进制字面量 → 十进制 | 0xff → 255 |
13.2.4 toPrecision --- 有效数字
toPrecision(precision) 指定有效数字的位数(包括整数部分和小数部分)。
javascript
const num = 123.456;
console.log(num.toPrecision(2)); // "1.2e+2"(科学计数法)
console.log(num.toPrecision(3)); // "123"
console.log(num.toPrecision(4)); // "123.5"
console.log(num.toPrecision(6)); // "123.456"
console.log(num.toPrecision(8)); // "123.45600"(补零)
toFixed vs toPrecision 的区别:
方法 控制内容 示例(num = 123.456) toFixed(2)小数点后位数 "123.46"toPrecision(2)总有效数字位数 "1.2e+2"
13.2.5 Number 构造函数的静态方法
Number 对象提供了一系列静态方法,用于判断数字的类别:
javascript
// Number.isInteger:判断是否为整数
console.log(Number.isInteger(42)); // true
console.log(Number.isInteger(42.0)); // true
console.log(Number.isInteger(42.5)); // false
console.log(Number.isInteger('42')); // false(不转换类型)
console.log(Number.isInteger(NaN)); // false
// Number.isFinite:判断是否为有限数
console.log(Number.isFinite(42)); // true
console.log(Number.isFinite(Infinity)); // false
console.log(Number.isFinite(NaN)); // false
console.log(Number.isFinite('42')); // false(不转换类型)
// Number.isNaN:判断是否为 NaN
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN(42)); // false
console.log(Number.isNaN('NaN')); // false(不转换类型)
console.log(Number.isNaN(undefined)); // false
13.2.6 全局方法 vs Number 静态方法
全局的 isFinite()、isNaN() 和 Number.isFinite()、Number.isNaN() 有重要区别:
javascript
// 全局 isNaN:会先尝试转换为数字,再判断
console.log(isNaN('hello')); // true('hello' 转为 NaN)
console.log(isNaN(undefined)); // true(undefined 转为 NaN)
console.log(isNaN('')); // false('' 转为 0)
console.log(isNaN('123')); // false('123' 转为 123)
// Number.isNaN:不转换类型,严格判断
console.log(Number.isNaN('hello')); // false(类型不是 number)
console.log(Number.isNaN(undefined)); // false
console.log(Number.isNaN(NaN)); // true(只有真正的 NaN 才返回 true)
// 全局 isFinite:会先转换
console.log(isFinite('42')); // true('42' 转为 42,是有限数)
console.log(isFinite(null)); // true(null 转为 0,是有限数)
// Number.isFinite:不转换
console.log(Number.isFinite('42')); // false(类型不是 number)
console.log(Number.isFinite(42)); // true
#mermaid-svg-KrzmALn7gUNjj6PH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KrzmALn7gUNjj6PH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KrzmALn7gUNjj6PH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KrzmALn7gUNjj6PH .error-icon{fill:#552222;}#mermaid-svg-KrzmALn7gUNjj6PH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KrzmALn7gUNjj6PH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KrzmALn7gUNjj6PH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KrzmALn7gUNjj6PH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KrzmALn7gUNjj6PH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KrzmALn7gUNjj6PH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KrzmALn7gUNjj6PH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KrzmALn7gUNjj6PH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KrzmALn7gUNjj6PH .marker.cross{stroke:#333333;}#mermaid-svg-KrzmALn7gUNjj6PH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KrzmALn7gUNjj6PH p{margin:0;}#mermaid-svg-KrzmALn7gUNjj6PH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KrzmALn7gUNjj6PH .cluster-label text{fill:#333;}#mermaid-svg-KrzmALn7gUNjj6PH .cluster-label span{color:#333;}#mermaid-svg-KrzmALn7gUNjj6PH .cluster-label span p{background-color:transparent;}#mermaid-svg-KrzmALn7gUNjj6PH .label text,#mermaid-svg-KrzmALn7gUNjj6PH span{fill:#333;color:#333;}#mermaid-svg-KrzmALn7gUNjj6PH .node rect,#mermaid-svg-KrzmALn7gUNjj6PH .node circle,#mermaid-svg-KrzmALn7gUNjj6PH .node ellipse,#mermaid-svg-KrzmALn7gUNjj6PH .node polygon,#mermaid-svg-KrzmALn7gUNjj6PH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KrzmALn7gUNjj6PH .rough-node .label text,#mermaid-svg-KrzmALn7gUNjj6PH .node .label text,#mermaid-svg-KrzmALn7gUNjj6PH .image-shape .label,#mermaid-svg-KrzmALn7gUNjj6PH .icon-shape .label{text-anchor:middle;}#mermaid-svg-KrzmALn7gUNjj6PH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KrzmALn7gUNjj6PH .rough-node .label,#mermaid-svg-KrzmALn7gUNjj6PH .node .label,#mermaid-svg-KrzmALn7gUNjj6PH .image-shape .label,#mermaid-svg-KrzmALn7gUNjj6PH .icon-shape .label{text-align:center;}#mermaid-svg-KrzmALn7gUNjj6PH .node.clickable{cursor:pointer;}#mermaid-svg-KrzmALn7gUNjj6PH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KrzmALn7gUNjj6PH .arrowheadPath{fill:#333333;}#mermaid-svg-KrzmALn7gUNjj6PH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KrzmALn7gUNjj6PH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KrzmALn7gUNjj6PH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KrzmALn7gUNjj6PH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KrzmALn7gUNjj6PH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KrzmALn7gUNjj6PH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KrzmALn7gUNjj6PH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KrzmALn7gUNjj6PH .cluster text{fill:#333;}#mermaid-svg-KrzmALn7gUNjj6PH .cluster span{color:#333;}#mermaid-svg-KrzmALn7gUNjj6PH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KrzmALn7gUNjj6PH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KrzmALn7gUNjj6PH rect.text{fill:none;stroke-width:0;}#mermaid-svg-KrzmALn7gUNjj6PH .icon-shape,#mermaid-svg-KrzmALn7gUNjj6PH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KrzmALn7gUNjj6PH .icon-shape p,#mermaid-svg-KrzmALn7gUNjj6PH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KrzmALn7gUNjj6PH .icon-shape .label rect,#mermaid-svg-KrzmALn7gUNjj6PH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KrzmALn7gUNjj6PH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KrzmALn7gUNjj6PH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KrzmALn7gUNjj6PH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
isNaN(value)
先转换为数字
结果 === NaN?
true
false
Number.isNaN(value)
typeof === 'number' 且值 === NaN?
true
false
| 方法 | 是否转换类型 | 推荐使用 |
|---|---|---|
isNaN() |
✅ 会先转换 | ❌ 容易产生误判 |
Number.isNaN() |
❌ 不转换 | ✅ 严格准确 |
isFinite() |
✅ 会先转换 | ❌ |
Number.isFinite() |
❌ 不转换 | ✅ |
最佳实践 :始终使用
Number.isNaN()和Number.isFinite(),避免全局方法的类型转换陷阱。
13.2.7 parseInt 与 parseFloat
parseInt 和 parseFloat 用于将字符串解析为数字,它们会从左到右逐字符解析,遇到无效字符时停止。
javascript
// parseInt:解析整数
console.log(parseInt('42')); // 42
console.log(parseInt('42.5')); // 42(小数部分被忽略)
console.log(parseInt('42px')); // 42(遇到非数字字符停止)
console.log(parseInt(' 42 ')); // 42(忽略前后空格)
console.log(parseInt('abc')); // NaN(第一个字符就不是数字)
console.log(parseInt('')); // NaN(空字符串)
console.log(parseInt('0xff')); // 255(十六进制前缀)
console.log(parseInt('0o17')); // 0(不识别八进制前缀,截断为 0)
console.log(parseInt('0b101')); // 0(不识别二进制前缀,截断为 0)
// parseFloat:解析浮点数
console.log(parseFloat('3.14')); // 3.14
console.log(parseFloat('3.14abc')); // 3.14
console.log(parseFloat('3.')); // 3
console.log(parseFloat('.5')); // 0.5
console.log(parseFloat('abc')); // NaN
parseInt 的进制参数:
javascript
// parseInt(str, radix):radix 指定字符串的进制
console.log(parseInt('10', 2)); // 2(二进制的 10 = 十进制的 2)
console.log(parseInt('10', 8)); // 8(八进制的 10 = 十进制的 8)
console.log(parseInt('10', 10)); // 10(十进制)
console.log(parseInt('10', 16)); // 16(十六进制的 10 = 十进制的 16)
console.log(parseInt('ff', 16)); // 255
// 不传 radix 的陷阱(旧版引擎中 '0' 开头会被解析为八进制)
console.log(parseInt('010')); // 10(现代引擎默认十进制)
console.log(parseInt('010', 10)); // 10(显式指定十进制)
最佳实践 :使用
parseInt时始终传入第二个参数radix,避免不同引擎的行为差异。
13.2.8 Number() vs parseInt() vs parseFloat()
三者都能将字符串转为数字,但行为不同:
javascript
// Number():严格解析,整个字符串必须是合法数字
console.log(Number('42')); // 42
console.log(Number('42.5')); // 42.5
console.log(Number('42px')); // NaN(包含非数字字符)
console.log(Number('')); // 0(空字符串转为 0)
console.log(Number(' ')); // 0(纯空格转为 0)
console.log(Number(null)); // 0
console.log(Number(undefined)); // NaN
console.log(Number(true)); // 1
console.log(Number(false)); // 0
// parseInt():宽松解析,遇到非数字字符停止
console.log(parseInt('42')); // 42
console.log(parseInt('42.5')); // 42
console.log(parseInt('42px')); // 42
console.log(parseInt('')); // NaN
console.log(parseInt(null)); // NaN
console.log(parseInt(true)); // NaN
// parseFloat():宽松解析,保留小数
console.log(parseFloat('42')); // 42
console.log(parseFloat('42.5')); // 42.5
console.log(parseFloat('42.5px')); // 42.5
console.log(parseFloat('')); // NaN
| 方法 | '42px' |
'' |
null |
true |
'3.14' |
|---|---|---|---|---|---|
Number() |
NaN |
0 |
0 |
1 |
3.14 |
parseInt() |
42 |
NaN |
NaN |
NaN |
3 |
parseFloat() |
42 |
NaN |
NaN |
NaN |
3.14 |
选择建议:
- 需要严格验证整个字符串是否为合法数字 →
Number()- 从混合字符串中提取整数 →
parseInt(str, 10)- 从混合字符串中提取浮点数 →
parseFloat(str)
13.2.9 数字的特殊值
javascript
// NaN:Not a Number(非数字)
console.log(typeof NaN); // "number"(NaN 的类型竟然是 number!)
console.log(NaN === NaN); // false(NaN 不等于自身)
console.log(NaN !== NaN); // true
console.log(Number.isNaN(NaN)); // true(正确的判断方式)
// Infinity:无穷大
console.log(1 / 0); // Infinity
console.log(-1 / 0); // -Infinity
console.log(typeof Infinity); // "number"
console.log(Infinity > 9999999); // true
// -0:负零
console.log(-0); // -0
console.log(0 === -0); // true(0 和 -0 相等)
console.log(Object.is(0, -0)); // false(Object.is 能区分)
console.log(1 / -0); // -Infinity(符号不同)
13.3 精度问题与 BigInt
13.3.1 IEEE 754 浮点数
JavaScript 中的所有数字(bigint 除外)都使用 IEEE 754 双精度浮点数(64-bit)格式存储。一个 64 位浮点数由三部分组成:
#mermaid-svg-1rDy0xe8tMCkKHrz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1rDy0xe8tMCkKHrz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1rDy0xe8tMCkKHrz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1rDy0xe8tMCkKHrz .error-icon{fill:#552222;}#mermaid-svg-1rDy0xe8tMCkKHrz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1rDy0xe8tMCkKHrz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1rDy0xe8tMCkKHrz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1rDy0xe8tMCkKHrz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1rDy0xe8tMCkKHrz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1rDy0xe8tMCkKHrz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1rDy0xe8tMCkKHrz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1rDy0xe8tMCkKHrz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1rDy0xe8tMCkKHrz .marker.cross{stroke:#333333;}#mermaid-svg-1rDy0xe8tMCkKHrz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1rDy0xe8tMCkKHrz p{margin:0;}#mermaid-svg-1rDy0xe8tMCkKHrz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1rDy0xe8tMCkKHrz .cluster-label text{fill:#333;}#mermaid-svg-1rDy0xe8tMCkKHrz .cluster-label span{color:#333;}#mermaid-svg-1rDy0xe8tMCkKHrz .cluster-label span p{background-color:transparent;}#mermaid-svg-1rDy0xe8tMCkKHrz .label text,#mermaid-svg-1rDy0xe8tMCkKHrz span{fill:#333;color:#333;}#mermaid-svg-1rDy0xe8tMCkKHrz .node rect,#mermaid-svg-1rDy0xe8tMCkKHrz .node circle,#mermaid-svg-1rDy0xe8tMCkKHrz .node ellipse,#mermaid-svg-1rDy0xe8tMCkKHrz .node polygon,#mermaid-svg-1rDy0xe8tMCkKHrz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1rDy0xe8tMCkKHrz .rough-node .label text,#mermaid-svg-1rDy0xe8tMCkKHrz .node .label text,#mermaid-svg-1rDy0xe8tMCkKHrz .image-shape .label,#mermaid-svg-1rDy0xe8tMCkKHrz .icon-shape .label{text-anchor:middle;}#mermaid-svg-1rDy0xe8tMCkKHrz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1rDy0xe8tMCkKHrz .rough-node .label,#mermaid-svg-1rDy0xe8tMCkKHrz .node .label,#mermaid-svg-1rDy0xe8tMCkKHrz .image-shape .label,#mermaid-svg-1rDy0xe8tMCkKHrz .icon-shape .label{text-align:center;}#mermaid-svg-1rDy0xe8tMCkKHrz .node.clickable{cursor:pointer;}#mermaid-svg-1rDy0xe8tMCkKHrz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1rDy0xe8tMCkKHrz .arrowheadPath{fill:#333333;}#mermaid-svg-1rDy0xe8tMCkKHrz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1rDy0xe8tMCkKHrz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1rDy0xe8tMCkKHrz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1rDy0xe8tMCkKHrz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1rDy0xe8tMCkKHrz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1rDy0xe8tMCkKHrz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1rDy0xe8tMCkKHrz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1rDy0xe8tMCkKHrz .cluster text{fill:#333;}#mermaid-svg-1rDy0xe8tMCkKHrz .cluster span{color:#333;}#mermaid-svg-1rDy0xe8tMCkKHrz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1rDy0xe8tMCkKHrz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1rDy0xe8tMCkKHrz rect.text{fill:none;stroke-width:0;}#mermaid-svg-1rDy0xe8tMCkKHrz .icon-shape,#mermaid-svg-1rDy0xe8tMCkKHrz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1rDy0xe8tMCkKHrz .icon-shape p,#mermaid-svg-1rDy0xe8tMCkKHrz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1rDy0xe8tMCkKHrz .icon-shape .label rect,#mermaid-svg-1rDy0xe8tMCkKHrz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1rDy0xe8tMCkKHrz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1rDy0xe8tMCkKHrz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1rDy0xe8tMCkKHrz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 64 bit 浮点数
符号位 1 bit
Sign
指数位 11 bit
Exponent
尾数位 52 bit
Mantissa
| 部分 | 位数 | 说明 |
|---|---|---|
| 符号位(Sign) | 1 bit | 0 正数,1 负数 |
| 指数位(Exponent) | 11 bit | 决定数值的范围(大小) |
| 尾数位(Mantissa) | 52 bit | 决定数值的精度(有效数字) |
由于尾数只有 52 位,JavaScript 能精确表示的整数范围是有限的:
javascript
// Number.MAX_SAFE_INTEGER:最大安全整数
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 (2^53 - 1)
// 超过这个范围,精度丢失
console.log(9007199254740992); // 9007199254740992(恰好能表示)
console.log(9007199254740993); // 9007199254740992 ← 精度丢失!与上面相同
// 判断是否在安全范围内
console.log(Number.isSafeInteger(9007199254740991)); // true
console.log(Number.isSafeInteger(9007199254740992)); // false
13.3.2 经典精度问题:0.1 + 0.2 !== 0.3
这是 JavaScript(以及所有使用 IEEE 754 的语言)中最著名的精度问题:
javascript
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
// 更多精度问题
console.log(0.3 - 0.1); // 0.19999999999999998
console.log(0.1 * 3); // 0.30000000000000004
console.log(0.1 + 0.7); // 0.7999999999999999
console.log(19.9 * 100); // 1989.9999999999998
原因分析:
十进制的 0.1 转换为二进制时是一个无限循环小数:
( 0.1 ) 10 = ( 0.0 0011 ‾ ) 2 = 0.0001100110011001100110011... (0.1){10} = (0.0\overline{0011}){2} = 0.0001100110011001100110011... (0.1)10=(0.00011)2=0.0001100110011001100110011...
由于尾数只有 52 位,这个无限循环小数被截断,产生了微小的误差。两个带有误差的数相加,误差被放大。
#mermaid-svg-ZCLWL46HIC4S62Vy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZCLWL46HIC4S62Vy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZCLWL46HIC4S62Vy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZCLWL46HIC4S62Vy .error-icon{fill:#552222;}#mermaid-svg-ZCLWL46HIC4S62Vy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZCLWL46HIC4S62Vy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZCLWL46HIC4S62Vy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZCLWL46HIC4S62Vy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZCLWL46HIC4S62Vy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZCLWL46HIC4S62Vy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZCLWL46HIC4S62Vy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZCLWL46HIC4S62Vy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZCLWL46HIC4S62Vy .marker.cross{stroke:#333333;}#mermaid-svg-ZCLWL46HIC4S62Vy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZCLWL46HIC4S62Vy p{margin:0;}#mermaid-svg-ZCLWL46HIC4S62Vy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZCLWL46HIC4S62Vy .cluster-label text{fill:#333;}#mermaid-svg-ZCLWL46HIC4S62Vy .cluster-label span{color:#333;}#mermaid-svg-ZCLWL46HIC4S62Vy .cluster-label span p{background-color:transparent;}#mermaid-svg-ZCLWL46HIC4S62Vy .label text,#mermaid-svg-ZCLWL46HIC4S62Vy span{fill:#333;color:#333;}#mermaid-svg-ZCLWL46HIC4S62Vy .node rect,#mermaid-svg-ZCLWL46HIC4S62Vy .node circle,#mermaid-svg-ZCLWL46HIC4S62Vy .node ellipse,#mermaid-svg-ZCLWL46HIC4S62Vy .node polygon,#mermaid-svg-ZCLWL46HIC4S62Vy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZCLWL46HIC4S62Vy .rough-node .label text,#mermaid-svg-ZCLWL46HIC4S62Vy .node .label text,#mermaid-svg-ZCLWL46HIC4S62Vy .image-shape .label,#mermaid-svg-ZCLWL46HIC4S62Vy .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZCLWL46HIC4S62Vy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZCLWL46HIC4S62Vy .rough-node .label,#mermaid-svg-ZCLWL46HIC4S62Vy .node .label,#mermaid-svg-ZCLWL46HIC4S62Vy .image-shape .label,#mermaid-svg-ZCLWL46HIC4S62Vy .icon-shape .label{text-align:center;}#mermaid-svg-ZCLWL46HIC4S62Vy .node.clickable{cursor:pointer;}#mermaid-svg-ZCLWL46HIC4S62Vy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZCLWL46HIC4S62Vy .arrowheadPath{fill:#333333;}#mermaid-svg-ZCLWL46HIC4S62Vy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZCLWL46HIC4S62Vy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZCLWL46HIC4S62Vy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZCLWL46HIC4S62Vy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZCLWL46HIC4S62Vy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZCLWL46HIC4S62Vy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZCLWL46HIC4S62Vy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZCLWL46HIC4S62Vy .cluster text{fill:#333;}#mermaid-svg-ZCLWL46HIC4S62Vy .cluster span{color:#333;}#mermaid-svg-ZCLWL46HIC4S62Vy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZCLWL46HIC4S62Vy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZCLWL46HIC4S62Vy rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZCLWL46HIC4S62Vy .icon-shape,#mermaid-svg-ZCLWL46HIC4S62Vy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZCLWL46HIC4S62Vy .icon-shape p,#mermaid-svg-ZCLWL46HIC4S62Vy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZCLWL46HIC4S62Vy .icon-shape .label rect,#mermaid-svg-ZCLWL46HIC4S62Vy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZCLWL46HIC4S62Vy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZCLWL46HIC4S62Vy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZCLWL46HIC4S62Vy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 十进制 0.1
转换为二进制
0.0001100110011...
无限循环
截断为 52 位尾数
精度丢失
存储值 ≈ 0.10000000000000000555
0.1 + 0.2 运算
0.30000000000000004
解决方案:
javascript
// 方案1:使用 Number.EPSILON 比较浮点数
const EPSILON = Number.EPSILON; // 2.220446049250313e-16
function isAlmostEqual(a, b) {
return Math.abs(a - b) < EPSILON;
}
console.log(isAlmostEqual(0.1 + 0.2, 0.3)); // true
// 方案2:转换为整数运算(推荐用于金额计算)
function addPrecise(a, b) {
// 找到两个数中最大的小数位数
const aDecimals = (a.toString().split('.')[1] || '').length;
const bDecimals = (b.toString().split('.')[1] || '').length;
const maxDecimals = Math.max(aDecimals, bDecimals);
const factor = Math.pow(10, maxDecimals);
return (Math.round(a * factor) + Math.round(b * factor)) / factor;
}
console.log(addPrecise(0.1, 0.2)); // 0.3
console.log(addPrecise(0.3, 0.1)); // 0.4
console.log(addPrecise(19.9, 0.1)); // 20
13.3.3 Number.EPSILON
Number.EPSILON 表示 1 与大于 1 的最小浮点数之间的差值,是 JavaScript 中能表示的最小精度:
javascript
console.log(Number.EPSILON); // 2.220446049250313e-16
// 用于浮点数相等比较
function floatEqual(a, b, epsilon = Number.EPSILON) {
return Math.abs(a - b) < epsilon;
}
console.log(floatEqual(0.1 + 0.2, 0.3)); // true
console.log(floatEqual(0.3 - 0.1, 0.2)); // true
13.3.4 数字的安全范围
javascript
// 最大安全整数
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
// 最小安全整数
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991
// 最大正数
console.log(Number.MAX_VALUE); // 1.7976931348623157e+308
// 最小正数(接近 0)
console.log(Number.MIN_VALUE); // 5e-324
// 正无穷 / 负无穷
console.log(Number.POSITIVE_INFINITY); // Infinity
console.log(Number.NEGATIVE_INFINITY); // -Infinity
| 常量 | 值 | 说明 |
|---|---|---|
Number.MAX_SAFE_INTEGER |
2 53 − 1 2^{53} - 1 253−1 | 最大安全整数 |
Number.MIN_SAFE_INTEGER |
− ( 2 53 − 1 ) -(2^{53} - 1) −(253−1) | 最小安全整数 |
Number.MAX_VALUE |
≈ 1.8 × 10 308 \approx 1.8 \times 10^{308} ≈1.8×10308 | 最大正数 |
Number.MIN_VALUE |
≈ 5 × 10 − 324 \approx 5 \times 10^{-324} ≈5×10−324 | 最小正数(最接近 0) |
Number.EPSILON |
≈ 2.22 × 10 − 16 \approx 2.22 \times 10^{-16} ≈2.22×10−16 | 浮点数最小精度 |
13.3.5 BigInt --- 大整数类型
BigInt 是 ES2020 引入的新原始类型,用于表示任意精度 的整数,解决了 Number 无法精确表示大整数的问题。
javascript
// 创建 BigInt:在数字末尾加 n
const big1 = 9007199254740993n;
console.log(big1); // 9007199254740993n(精确!)
// 使用 BigInt() 函数转换
const big2 = BigInt('9007199254740993');
console.log(big2); // 9007199254740993n
const big3 = BigInt(123);
console.log(big3); // 123n
// typeof 检测
console.log(typeof big1); // "bigint"
BigInt 的运算:
javascript
const a = 12345678901234567890n;
const b = 98765432109876543210n;
// 基本运算
console.log(a + b); // 111111111011111111100n
console.log(a - b); // -86419753208641975320n
console.log(a * b); // 1219326311370217952237463801111263526900n
console.log(a / b); // 0n(整数除法,截断小数)
console.log(a % b); // 12345678901234567890n
// 比较
console.log(1n < 2n); // true
console.log(2n > 1); // true(可以与 number 比较)
console.log(1n === 1); // false(类型不同)
console.log(1n == 1); // true(宽松相等,会转换)
BigInt 的限制:
javascript
// 1. 不能与 number 直接运算
// console.log(1n + 1); // TypeError: Cannot mix BigInt and other types
// 需要显式转换
console.log(1n + BigInt(1)); // 2n
console.log(Number(1n) + 1); // 2
// 2. 不支持小数(结果是整数除法)
console.log(7n / 2n); // 3n(不是 3.5)
// 3. 不能用 Math 对象
// Math.sqrt(16n); // TypeError
// 4. 不能与 JSON 互转
// JSON.stringify(1n); // TypeError(默认不支持)
// 需要自定义 toJSON
BigInt.prototype.toJSON = function() { return Number(this); };
console.log(JSON.stringify({ count: 100n })); // '{"count":100}'
| 特性 | Number |
BigInt |
|---|---|---|
| 精度 | 最多 53 位整数 | 任意精度 |
| 小数 | ✅ 支持 | ❌ 不支持 |
| 与 number 混合运算 | --- | ❌ 需显式转换 |
Math 方法 |
✅ 支持 | ❌ 不支持 |
JSON.stringify |
✅ | ❌(需自定义) |
typeof |
"number" |
"bigint" |
使用建议 :只有在需要处理超过
Number.MAX_SAFE_INTEGER的大整数时才使用BigInt。日常的小数计算用整数运算方案或专门的库(如decimal.js)更合适。
13.3.6 Number 与 BigInt 的互转
javascript
// BigInt → Number(可能丢失精度)
const big = 9007199254740993n;
const num = Number(big);
console.log(num); // 9007199254740992(精度丢失!)
// 安全的转换:先检查范围
function bigIntToNumber(big) {
if (big > Number.MAX_SAFE_INTEGER || big < Number.MIN_SAFE_INTEGER) {
console.warn('BigInt 超出安全范围,转换可能导致精度丢失');
}
return Number(big);
}
// Number → BigInt
const n = 42;
const b = BigInt(n);
console.log(b); // 42n
// 注意:浮点数转 BigInt 会截断小数
console.log(BigInt(3.9)); // 3n(截断,不四舍五入)
// console.log(BigInt(3.14)); // RangeError(某些引擎中)
13.4 字符串与布尔的装箱方法
13.4.1 字符串的装箱方法
字符串原始类型在调用方法时会装箱为 String 对象。本章简要概述,详细方法将在第 14 章深入讲解。
javascript
const str = 'Hello World';
// 这些方法都是通过装箱机制调用的
console.log(str.length); // 11(访问属性也会装箱)
console.log(str.toUpperCase()); // "HELLO WORLD"
console.log(str.toLowerCase()); // "hello world"
console.log(str.charAt(0)); // "H"
console.log(str.indexOf('o')); // 4
console.log(str.slice(0, 5)); // "Hello"
console.log(str.split(' ')); // ['Hello', 'World']
console.log(str.includes('World')); // true
console.log(str.replace('World', 'JS')); // "Hello JS"
13.4.2 布尔的装箱方法
布尔原始类型的方法很少,主要是 toString 和 valueOf:
javascript
const bool = true;
console.log(bool.toString()); // "true"
console.log(bool.valueOf()); // true
const bool2 = false;
console.log(bool2.toString()); // "false"
// 装箱后的 Boolean 对象有严重陷阱
const wrappedTrue = new Boolean(true);
const wrappedFalse = new Boolean(false);
console.log(typeof wrappedTrue); // "object"
console.log(wrappedTrue === true); // false
console.log(wrappedTrue == true); // true
// ⚠️ 最危险的陷阱:Boolean 对象在条件判断中永远为 true
if (new Boolean(false)) {
console.log('这会执行!'); // 因为对象总是 truthy
}
if (new Boolean(false).valueOf()) {
console.log('这不会执行');
}
强烈警告 :永远不要使用
new Boolean()创建布尔对象。if (new Boolean(false))会进入if分支,因为任何对象都是 truthy 值。
13.4.3 Symbol 的装箱
Symbol 是 ES6 引入的原始类型,它有一个特殊之处:不能使用 new Symbol() 创建包装对象。
javascript
const sym = Symbol('test');
// typeof
console.log(typeof sym); // "symbol"
// 装箱方法
console.log(sym.toString()); // "Symbol(test)"
console.log(sym.description); // "test"
// Symbol 不能用 new
// const wrapped = new Symbol('test'); // TypeError: Symbol is not a constructor
// 但可以用 Object() 显式装箱
const wrappedSym = Object(sym);
console.log(typeof wrappedSym); // "object"
console.log(wrappedSym instanceof Symbol); // true
13.5 实战
13.5.1 实战1:金额格式化
实现一个金额格式化工具,支持千分位分隔、小数保留、货币符号。
javascript
/**
* 金额格式化工具
*/
class MoneyFormatter {
/**
* @param {string} currency - 货币符号
* @param {number} decimals - 小数位数
*/
constructor(currency = '¥', decimals = 2) {
this.currency = currency;
this.decimals = decimals;
}
/**
* 格式化金额为千分位字符串
* @param {number} amount - 金额
* @returns {string} 格式化后的金额字符串
*/
format(amount) {
if (!Number.isFinite(amount)) {
return `${this.currency}0.${'0'.repeat(this.decimals)}`;
}
// 精确舍入
const factor = Math.pow(10, this.decimals);
const rounded = Math.round((amount + Number.EPSILON) * factor) / factor;
// 分离整数和小数部分
const [intPart, decPart] = rounded.toFixed(this.decimals).split('.');
// 千分位分隔
const formattedInt = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return `${this.currency}${formattedInt}.${decPart}`;
}
/**
* 解析金额字符串为数字
* @param {string} str - 金额字符串
* @returns {number} 解析后的金额
*/
parse(str) {
const cleaned = str.replace(/[¥$,\s]/g, '');
const num = parseFloat(cleaned);
return Number.isFinite(num) ? num : 0;
}
}
// ===== 测试 =====
const formatter = new MoneyFormatter('¥', 2);
console.log(formatter.format(1234567.89)); // "¥1,234,567.89"
console.log(formatter.format(999.999)); // "¥1,000.00"(进位)
console.log(formatter.format(0.1)); // "¥0.10"
console.log(formatter.format(-1234.5)); // "¥-1,234.50"
console.log(formatter.format(0)); // "¥0.00"
// 解析
console.log(formatter.parse('¥1,234,567.89')); // 1234567.89
console.log(formatter.parse('$1,000.00')); // 1000
// 使用 Intl.NumberFormat(浏览器原生方案)
const intlFormatter = new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY',
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
console.log(intlFormatter.format(1234567.89)); // "¥1,234,567.89"
console.log(intlFormatter.format(0.1)); // "¥0.10"
13.5.2 实战2:精度安全计算
实现一个精确的加减乘除运算工具,解决浮点数精度问题。
javascript
/**
* 精度安全计算工具
* 解决 JavaScript 浮点数精度问题
*/
const Decimal = {
/**
* 获取数字的小数位数
* @param {number} num
* @returns {number}
*/
_getDecimals(num) {
const str = num.toString();
const scientific = str.indexOf('e-');
if (scientific !== -1) {
return parseInt(str.slice(scientific + 2));
}
const dotIndex = str.indexOf('.');
return dotIndex === -1 ? 0 : str.length - dotIndex - 1;
},
/**
* 将浮点数转换为整数(保留精度)
* @param {number} num
* @returns {{ value: number, decimals: number }}
*/
_toInt(num) {
const decimals = this._getDecimals(num);
const str = num.toFixed(decimals);
return {
value: parseInt(str.replace('.', '')),
decimals
};
},
/**
* 精确加法
* @param {number} a
* @param {number} b
* @returns {number}
*/
add(a, b) {
const aInt = this._toInt(a);
const bInt = this._toInt(b);
const maxDecimals = Math.max(aInt.decimals, bInt.decimals);
const factor = Math.pow(10, maxDecimals);
const aScaled = aInt.value * Math.pow(10, maxDecimals - aInt.decimals);
const bScaled = bInt.value * Math.pow(10, maxDecimals - bInt.decimals);
return (aScaled + bScaled) / factor;
},
/**
* 精确减法
* @param {number} a
* @param {number} b
* @returns {number}
*/
subtract(a, b) {
return this.add(a, -b);
},
/**
* 精确乘法
* @param {number} a
* @param {number} b
* @returns {number}
*/
multiply(a, b) {
const aInt = this._toInt(a);
const bInt = this._toInt(b);
const result = aInt.value * bInt.value;
const factor = Math.pow(10, aInt.decimals + bInt.decimals);
return result / factor;
},
/**
* 精确除法
* @param {number} a
* @param {number} b
* @returns {number}
*/
divide(a, b) {
if (b === 0) {
throw new Error('除数不能为零');
}
const aInt = this._toInt(a);
const bInt = this._toInt(b);
const result = (aInt.value / bInt.value);
const factor = Math.pow(10, aInt.decimals - bInt.decimals);
return result / factor;
},
/**
* 精确四舍五入
* @param {number} num
* @param {number} digits
* @returns {number}
*/
round(num, digits = 0) {
const factor = Math.pow(10, digits);
return Math.round((num + Number.EPSILON) * factor) / factor;
}
};
// ===== 测试 =====
// 加法
console.log(Decimal.add(0.1, 0.2)); // 0.3(原生 0.30000000000000004)
console.log(Decimal.add(19.9, 0.1)); // 20(原生 19.999999999999996)
console.log(Decimal.add(0.001, 0.002)); // 0.003
// 减法
console.log(Decimal.subtract(0.3, 0.1)); // 0.2(原生 0.19999999999999998)
console.log(Decimal.subtract(1.5, 1.2)); // 0.3
// 乘法
console.log(Decimal.multiply(0.1, 3)); // 0.3(原生 0.30000000000000004)
console.log(Decimal.multiply(19.9, 100)); // 1990(原生 1989.9999999999998)
console.log(Decimal.multiply(0.07, 100)); // 7
// 除法
console.log(Decimal.divide(0.3, 0.1)); // 3(原生 2.9999999999999996)
console.log(Decimal.divide(1, 3)); // 0.3333333333333333
// 四舍五入
console.log(Decimal.round(1.005, 2)); // 1.01(原生 toFixed 得到 "1.00")
console.log(Decimal.round(1.025, 2)); // 1.03
console.log(Decimal.round(2.5, 0)); // 3
13.5.3 实战3:进制转换工具
javascript
/**
* 进制转换工具
* 支持 2~36 进制之间的互相转换
*/
class RadixConverter {
/**
* 十进制转其他进制
* @param {number} decimal - 十进制数字
* @param {number} radix - 目标进制(2~36)
* @returns {string} 转换后的字符串
*/
static fromDecimal(decimal, radix) {
if (!Number.isInteger(decimal)) {
throw new Error('只支持整数转换');
}
if (radix < 2 || radix > 36) {
throw new Error('进制范围必须在 2~36 之间');
}
return decimal.toString(radix).toUpperCase();
}
/**
* 其他进制转十进制
* @param {string} str - 源字符串
* @param {number} radix - 源进制(2~36)
* @returns {number} 十进制数字
*/
static toDecimal(str, radix) {
const result = parseInt(str, radix);
if (Number.isNaN(result)) {
throw new Error(`"${str}" 不是合法的 ${radix} 进制数`);
}
return result;
}
/**
* 任意进制互转
* @param {string} str - 源字符串
* @param {number} fromRadix - 源进制
* @param {number} toRadix - 目标进制
* @returns {string} 转换后的字符串
*/
static convert(str, fromRadix, toRadix) {
const decimal = this.toDecimal(str, fromRadix);
return this.fromDecimal(decimal, toRadix);
}
/**
* 获取数字的完整进制信息
* @param {number} num - 十进制数字
* @returns {Object} 包含各种进制的对象
*/
static getInfo(num) {
return {
decimal: num.toString(),
binary: num.toString(2),
octal: num.toString(8),
hex: num.toString(16).toUpperCase(),
base36: num.toString(36).toUpperCase()
};
}
}
// ===== 测试 =====
// 十进制转其他进制
console.log(RadixConverter.fromDecimal(255, 2)); // "11111111"
console.log(RadixConverter.fromDecimal(255, 8)); // "377"
console.log(RadixConverter.fromDecimal(255, 16)); // "FF"
console.log(RadixConverter.fromDecimal(255, 36)); // "73"
// 其他进制转十进制
console.log(RadixConverter.toDecimal('11111111', 2)); // 255
console.log(RadixConverter.toDecimal('FF', 16)); // 255
// 任意进制互转
console.log(RadixConverter.convert('FF', 16, 2)); // "11111111"(十六进制 → 二进制)
console.log(RadixConverter.convert('11111111', 2, 16)); // "FF"(二进制 → 十六进制)
console.log(RadixConverter.convert('73', 36, 10)); // "255"(三十六进制 → 十进制)
// 获取完整进制信息
console.log(RadixConverter.getInfo(255));
// { decimal: "255", binary: "11111111", octal: "377", hex: "FF", base36: "73" }
13.5.4 实战4:大数计算器
利用 BigInt 实现大整数的阶乘、幂运算和组合数计算。
javascript
/**
* 大数计算器
* 基于 BigInt 实现超大整数运算
*/
class BigCalculator {
/**
* 阶乘 n!
* @param {number} n - 非负整数
* @returns {bigint} n 的阶乘
*/
static factorial(n) {
if (n < 0) {
throw new Error('阶乘只支持非负整数');
}
if (n === 0 || n === 1) {
return 1n;
}
let result = 1n;
for (let i = 2n; i <= BigInt(n); i++) {
result *= i;
}
return result;
}
/**
* 幂运算 base^exp
* @param {bigint} base - 底数
* @param {number} exp - 指数(非负整数)
* @returns {bigint}
*/
static power(base, exp) {
if (exp < 0) {
throw new Error('指数只支持非负整数');
}
if (exp === 0) {
return 1n;
}
// 快速幂算法
let result = 1n;
let b = BigInt(base);
let e = BigInt(exp);
while (e > 0n) {
if (e % 2n === 1n) {
result *= b;
}
b *= b;
e /= 2n;
}
return result;
}
/**
* 组合数 C(n, k)
* @param {number} n
* @param {number} k
* @returns {bigint}
*/
static combination(n, k) {
if (k < 0 || k > n) {
return 0n;
}
if (k === 0 || k === n) {
return 1n;
}
// C(n, k) = C(n, n-k),取较小的 k 提高效率
if (k > n - k) {
k = n - k;
}
let result = 1n;
for (let i = 0; i < k; i++) {
result *= BigInt(n - i);
result /= BigInt(i + 1);
}
return result;
}
/**
* 斐波那契数列(大数版)
* @param {number} n - 第 n 项
* @returns {bigint}
*/
static fibonacci(n) {
if (n < 0) {
throw new Error('只支持非负整数');
}
if (n <= 1) {
return BigInt(n);
}
let prev = 0n;
let curr = 1n;
for (let i = 2; i <= n; i++) {
const next = prev + curr;
prev = curr;
curr = next;
}
return curr;
}
/**
* 格式化大数输出(千分位)
* @param {bigint} num
* @returns {string}
*/
static format(num) {
const str = num.toString();
const isNegative = str.startsWith('-');
const digits = isNegative ? str.slice(1) : str;
const formatted = digits.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return isNegative ? `-${formatted}` : formatted;
}
}
// ===== 测试 =====
// 阶乘(普通 number 会溢出)
console.log(BigCalculator.factorial(20).toString());
// "2432902008176640000"
console.log(BigCalculator.factorial(50).toString());
// "30414093201713378043612608166064768844377641568960512000000000000"
console.log(BigCalculator.format(BigCalculator.factorial(50)));
// "30,414,093,201,713,378,043,612,608,166,064,768,844,377,641,568,960,512,000,000,000,000"
// 幂运算
console.log(BigCalculator.power(2n, 100).toString());
// "1267650600228229401496703205376"(2^100,远超 Number.MAX_SAFE_INTEGER)
// 组合数
console.log(BigCalculator.combination(100, 50).toString());
// "100891344545564193334812497256"
// 斐波那契
console.log(BigCalculator.fibonacci(100).toString());
// "354224848179261915075"
console.log(BigCalculator.format(BigCalculator.fibonacci(100)));
// "354,224,848,179,261,915,075"
13.5.5 实战5:数字类型检测与安全转换
javascript
/**
* 数字安全工具
* 提供严格的数字检测和安全转换
*/
const SafeNumber = {
/**
* 检查值是否为有限数字
* @param {*} value
* @returns {boolean}
*/
isNumber(value) {
return typeof value === 'number' && Number.isFinite(value);
},
/**
* 检查值是否为安全整数
* @param {*} value
* @returns {boolean}
*/
isSafeInteger(value) {
return typeof value === 'number' && Number.isSafeInteger(value);
},
/**
* 安全转换为数字
* @param {*} value - 要转换的值
* @param {number} [fallback=0] - 转换失败时的默认值
* @returns {number}
*/
toNumber(value, fallback = 0) {
if (typeof value === 'number') {
return Number.isFinite(value) ? value : fallback;
}
if (typeof value === 'string') {
const trimmed = value.trim();
if (trimmed === '') return fallback;
const num = Number(trimmed);
return Number.isFinite(num) ? num : fallback;
}
if (typeof value === 'boolean') {
return value ? 1 : 0;
}
if (value === null) {
return 0;
}
if (value === undefined) {
return fallback;
}
if (typeof value === 'bigint') {
if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) {
console.warn('BigInt 超出安全范围,转换可能丢失精度');
}
return Number(value);
}
return fallback;
},
/**
* 安全转换为整数
* @param {*} value
* @param {number} [fallback=0]
* @returns {number}
*/
toInteger(value, fallback = 0) {
const num = this.toNumber(value, NaN);
if (Number.isNaN(num)) return fallback;
return Math.trunc(num);
},
/**
* 将数字限制在指定范围内
* @param {number} value
* @param {number} min
* @param {number} max
* @returns {number}
*/
clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
},
/**
* 生成指定范围内的随机整数
* @param {number} min
* @param {number} max
* @returns {number}
*/
randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
/**
* 数字简写格式(K、M、B)
* @param {number} num
* @returns {string}
*/
abbreviate(num) {
if (!this.isNumber(num)) return '0';
const abs = Math.abs(num);
if (abs >= 1e9) return (num / 1e9).toFixed(1) + 'B';
if (abs >= 1e6) return (num / 1e6).toFixed(1) + 'M';
if (abs >= 1e3) return (num / 1e3).toFixed(1) + 'K';
return num.toString();
}
};
// ===== 测试 =====
// 检测
console.log(SafeNumber.isNumber(42)); // true
console.log(SafeNumber.isNumber(NaN)); // false
console.log(SafeNumber.isNumber('42')); // false
console.log(SafeNumber.isSafeInteger(42)); // true
console.log(SafeNumber.isSafeInteger(9007199254740992)); // false
// 安全转换
console.log(SafeNumber.toNumber('42')); // 42
console.log(SafeNumber.toNumber('42px')); // 0(NaN → fallback)
console.log(SafeNumber.toNumber('42px', -1)); // -1(自定义 fallback)
console.log(SafeNumber.toNumber('')); // 0
console.log(SafeNumber.toNumber(null)); // 0
console.log(SafeNumber.toNumber(undefined)); // 0
console.log(SafeNumber.toNumber(true)); // 1
console.log(SafeNumber.toNumber(100n)); // 100
// 整数转换
console.log(SafeNumber.toInteger('42.9')); // 42
console.log(SafeNumber.toInteger('-3.7')); // -3
console.log(SafeNumber.toInteger('abc')); // 0
// 范围限制
console.log(SafeNumber.clamp(15, 0, 10)); // 10
console.log(SafeNumber.clamp(-5, 0, 10)); // 0
console.log(SafeNumber.clamp(5, 0, 10)); // 5
// 随机整数
console.log(SafeNumber.randomInt(1, 100)); // 1~100 的随机整数
// 数字简写
console.log(SafeNumber.abbreviate(999)); // "999"
console.log(SafeNumber.abbreviate(1234)); // "1.2K"
console.log(SafeNumber.abbreviate(1000000)); // "1.0M"
console.log(SafeNumber.abbreviate(1500000000)); // "1.5B"
13.6 原始类型方法速查表
13.6.1 数字方法速查
| 方法 | 说明 | 示例 | 返回 |
|---|---|---|---|
toFixed(n) |
保留 n 位小数 | (3.14159).toFixed(2) |
"3.14" |
toString(radix) |
转为指定进制字符串 | (255).toString(16) |
"ff" |
toPrecision(n) |
保留 n 位有效数字 | (123.456).toPrecision(3) |
"123" |
toExponential(n) |
科学计数法 | (12345).toExponential(2) |
"1.23e+4" |
Number.isInteger(x) |
是否为整数 | Number.isInteger(42) |
true |
Number.isFinite(x) |
是否为有限数 | Number.isFinite(42) |
true |
Number.isNaN(x) |
是否为 NaN | Number.isNaN(NaN) |
true |
Number.isSafeInteger(x) |
是否为安全整数 | Number.isSafeInteger(2**53) |
false |
parseInt(str, radix) |
字符串转整数 | parseInt('ff', 16) |
255 |
parseFloat(str) |
字符串转浮点数 | parseFloat('3.14') |
3.14 |
13.6.2 Number 静态常量速查
| 常量 | 值 | 说明 |
|---|---|---|
Number.MAX_SAFE_INTEGER |
2 53 − 1 2^{53} - 1 253−1 | 最大安全整数 |
Number.MIN_SAFE_INTEGER |
− ( 2 53 − 1 ) -(2^{53} - 1) −(253−1) | 最小安全整数 |
Number.MAX_VALUE |
≈ 1.8 × 10 308 \approx 1.8 \times 10^{308} ≈1.8×10308 | 最大正数 |
Number.MIN_VALUE |
≈ 5 × 10 − 324 \approx 5 \times 10^{-324} ≈5×10−324 | 最小正数 |
Number.EPSILON |
≈ 2.22 × 10 − 16 \approx 2.22 \times 10^{-16} ≈2.22×10−16 | 浮点数最小精度 |
Number.POSITIVE_INFINITY |
Infinity |
正无穷 |
Number.NEGATIVE_INFINITY |
-Infinity |
负无穷 |
Number.NaN |
NaN |
非数字 |
13.6.3 转换方法对比
| 方法 | 输入 | 输出 | 特点 |
|---|---|---|---|
Number(str) |
'42px' |
NaN |
严格,整体必须合法 |
parseInt(str, 10) |
'42px' |
42 |
宽松,逐字符解析 |
parseFloat(str) |
'3.14px' |
3.14 |
宽松,支持小数 |
+str |
'42' |
42 |
等价于 Number() |
str * 1 |
'42' |
42 |
等价于 Number() |
BigInt(str) |
'9007199254740993' |
9007199254740993n |
精确大整数 |
本章小结
| 知识点 | 要点 |
|---|---|
| 装箱机制 | 原始类型调用方法时,引擎临时创建包装对象,方法执行后立即销毁 |
null / undefined |
没有包装对象,调用方法会报 TypeError |
| 包装对象 | 不要手动 new Number() / new String() / new Boolean() |
toFixed(n) |
保留 n 位小数,返回字符串;存在精度陷阱 |
toString(radix) |
转换为指定进制字符串,支持 2~36 进制 |
parseInt(str, radix) |
从字符串解析整数,建议始终传入 radix |
Number() vs parseInt() |
前者严格(整体合法),后者宽松(逐字符解析) |
Number.isNaN / Number.isFinite |
不做类型转换,比全局 isNaN / isFinite 更安全 |
| IEEE 754 | 所有 number 都是 64 位浮点数,导致精度问题 |
0.1 + 0.2 !== 0.3 |
浮点数精度问题的经典案例,用 Number.EPSILON 比较 |
Number.MAX_SAFE_INTEGER |
2 53 − 1 2^{53} - 1 253−1,超过此范围整数精度丢失 |
BigInt |
任意精度整数,用 n 后缀或 BigInt() 创建;不能与 number 混合运算 |
一句话总结 :JavaScript 的原始类型通过隐式"装箱"机制获得方法调用能力,但包装对象是临时的、不可持久的;数字类型的方法(
toFixed、toString、parseInt)在日常开发中极为常用,而 IEEE 754 浮点数的精度问题需要通过整数运算、Number.EPSILON或BigInt来规避。
下篇预告 :第14章:字符串 --- 我们将深入 JavaScript 字符串的完整方法体系,学习模板字面量、标签模板、Unicode 处理,以及如何构建简易模板引擎。
作者 :xhmico | CSDN 博客 :https://blog.csdn.net/xhmico
参考资源 :The Modern JavaScript Tutorial | MDN Web Docs | ECMAScript 规范