原始类型的方法
我们来看看原始类型和对象之间的关键区别。
一个原始值:
- 是原始类型中的一种值。
- 在 JavaScript 中有 7 种原始类型:
string
,number
,bigint
,boolean
,symbol
,null
和undefined
。
一个对象:
-
能够存储多个值作为属性。
-
可以使用大括号
{}
创建对象,例如:{name: "John", age: 30}
。JavaScript 中还有其他种类的对象,例如函数就是对象。
关于对象的最好的事儿之一是,我们可以把一个函数作为对象的属性存储到对象中。
let john = {
name: "John",
sayHi: function() {
alert("Hi buddy!");
}
};
john.sayHi(); // Hi buddy!
当作对象的原始类型
let str = "Hello";
alert( str.toUpperCase() ); // HELLO
//
let n = 1.23456;
alert( n.toFixed(2) ); // 1.23
**注意:**构造器 String/Number/Boolean 仅供内部使用
像 Java 这样的一些语言允许我们使用 new Number(1)
或 new Boolean(false)
等语法,明确地为原始类型创建“对象包装器”。
在 JavaScript 中,由于历史原因,这也是可以的,但极其 不推荐。因为这样会出问题。
例如:
alert( typeof 0 ); // "number"
alert( typeof new Number(0) ); // "object"!
//对象在 if 语句中始终为真,因此此处的 alert 将显示
let zero = new Number(0);
if (zero) { // zero 为 true,因为它是一个对象
alert( "zero is truthy?!?" );
}
null/undefined 没有任何方法
特殊的原始类型 null
和 undefined
是例外。它们没有对应的“对象包装器”,也没有提供任何方法。从某种意义上说,它们是“最原始的”。
尝试访问这种值的属性会导致错误:
alert(null.test); //error
数字类型
在现代 JavaScript 中,数字(number)有两种类型:
- JavaScript 中的常规数字以 64 位的格式 IEEE-754 存储,也被称为“双精度浮点数”。这是我们大多数时候所使用的数字,我们将在本章中学习它们。
- BigInt 数字,用于表示任意长度的整数。有时会需要它们,因为常规数字不能超过
253
或小于-253
。由于仅在少数特殊领域才会用到 BigInt,因此我们在特殊的章节 BigInt 中对其进行了介绍。
编写数字的更多方法
在 JS 中,我们可以通过在数字后面附加字母 “e”,并指定零的数量来缩短数字:
let billion = 1e9; // 10 亿,字面意思:数字 1 后面跟 9 个 0
alert( 7.3e9 ); // 73 亿(7,300,000,000)
//1e3 = 1 * 1000
//1.23e6 = 1.23 * 1000000
let ms = 1e-6; // 1 的左边有 6 个 0
// -3 除以 1 后面跟着 3 个 0 的数字
1e-3 = 1 / 1000 (=0.001)
// -6 除以 1 后面跟着 6 个 0 的数字
1.23e-6 = 1.23 / 1000000 (=0.00000123)
十六进制,二进制和八进制数字
alert( 0xff ); // 255
alert( 0xFF ); // 255(一样,大小写没影响)
//二进制和八进制数字系统很少使用,但也支持使用 0b 和 0o 前缀:
let a = 0b11111111; // 二进制形式的 255
let b = 0o377; // 八进制形式的 255
alert( a == b ); // true,两边是相同的数字,都是 255
toString(base)
方法 num.toString(base) 返回在给定 base 进制数字系统中 num 的字符串表示形式。
let num = 255;
alert( num.toString(16) ); // ff
alert( num.toString(2) ); // 11111111
base 的范围可以从 2 到 36。默认情况下是 10。
举例:
- base=16 用于十六进制颜色,字符编码等,数字可以是
0..9
或A..F
。 - base=2 主要用于调试按位操作,数字可以是
0
或1
。 - base=36 是最大进制,数字可以是
0..9
或A..Z
。所有拉丁字母都被用于了表示数字。对于36
进制来说,一个有趣且有用的例子是,当我们需要将一个较长的数字标识符转换成较短的时候,例如做一个短的 URL。可以简单地使用基数为36
的数字系统表示:
alert( 123456..toString(36) ); // 2n9c
注意:使用两个点来调用一个方法
请注意 123456..toString(36)
中的两个点不是打错了。如果我们想直接在一个数字上调用一个方法,比如上面例子中的 toString
,那么我们需要在它后面放置两个点 ..
。
如果我们放置一个点:123456.toString(36)
,那么就会出现一个 error,因为 JavaScript 语法隐含了第一个点之后的部分为小数部分。如果我们再放一个点,那么 JavaScript 就知道小数部分为空,现在使用该方法。
也可以写成 (123456).toString(36)
。
舍入
-
Math.floor
向下舍入:
3.1
变成3
,-1.1
变成-2
。 -
Math.ceil
向上舍入:
3.1
变成4
,-1.1
变成-1
。 -
Math.round
向最近的整数舍入:
3.1
变成3
,3.6
变成4
,-1.1
变成-1
。 -
Math.trunc
(IE 浏览器不支持这个方法)移除小数点后的所有内容而没有舍入:
3.1
变成3
,-1.1
变成-1
。
这个是总结它们之间差异的表格:
Math.floor | Math.ceil | Math.round | Math.trunc | |
---|---|---|---|---|
3.1 | 3 | 4 | 3 | 3 |
3.6 | 3 | 4 | 4 | 3 |
-1.1 | -2 | -1 | -1 | -1 |
-1.6 | -2 | -1 | -2 | -1 |
- 乘除法
要将数字舍入到小数点后两位,可以将数字乘以 100,调用舍入函数,然后再将其除回。
let num = 1.23456;
alert( Math.floor(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23
- 函数 toFixed(n) 将数字舍入到小数点后
n
位,并以字符串形式返回结果。
let num = 12.34;
alert( num.toFixed(1) ); // "12.3"
这会向上或向下舍入到最接近的值,类似于 Math.round
let num = 12.36;
alert( num.toFixed(1) ); // "12.4"
注意到 toFixed 的结果是一个字符串。如果小数部分比所需要的短,则在结尾添加零:
let num = 12.34;
alert( num.toFixed(5) ); // "12.34000",在结尾添加了 0,以达到小数点后五位
不精确的计算
如果一个数字太大,则会溢出64位存储,并可能导致无穷大
alert( 1e500 ); // Infinity
这可能不太明显,但经常会发生的是,精度的损失。
测试一下:
alert( 0.1 + 0.2 == 0.3 ); // false
//其实呢
alert( 0.1 + 0.2 ); // 0.30000000000000004
使用二进制数字系统无法 精确 存储 0.1 或 0.2,就像没有办法将三分之一存储为十进制小数一样。
舍入规则不允许看到“极小的精度损失”
就可以看到
alert( 0.1.toFixed(20) ); // 0.10000000000000000555
如何解决这个问题呢,最可靠的方法就是借助方法 toFixed(n) 对结果进行舍入
let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // 0.30
//注意,toFixed 返回一个字符串,所以用一元符号强制转换为一个数字
let sum = 0.1 + 0.2;
alert( +sum.toFixed(2) ); // 0.3
当我们使用整数进行数学运算时,误差会有所减少,但仍然可以在除法中得到:
alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001
乘/除法可以减少误差,但不能完全消除误差。
有时候我们可以尝试完全避免小数。
看个例子
// Hello!我是一个会自我增加的数字!
alert( 9999999999999999 ); // 显示 10000000000000000
出现了同样的问题:精度损失。有 64 位来表示该数字,其中 52 位可用于存储数字,但这还不够。所以最不重要的数字就消失了。
JavaScript 不会在此类事件中触发 error。它会尽最大努力使数字符合所需的格式,但不幸的是,这种格式不够大到满足需求。
两个零
数字内部表示的另一个有趣结果是存在两个零:0
和 -0
。
这是因为在存储时,使用一位来存储符号,因此对于包括零在内的任何数字,可以设置这一位或者不设置。
在大多数情况下,这种区别并不明显,因为运算符将它们视为相同的值。
测试:isFinite 和 isNaN
Infinity
(和-Infinity
)是一个特殊的数值,比任何数值都大(小)。NaN
代表一个 error。
isNaN(value)
将其参数转换为数字,然后测试它是否为 NaN
:
alert( isNaN(NaN) ); // true
alert( isNaN("str") ); // true
//值 NaN 是独一无二的,不等于任何东西,包括它本身
alert( NaN === NaN ); // false
isFinite(value)
将其参数转换为数字,如果是常规数字,则返回 true
,而不是 NaN/Infinity/-Infinity
:
alert( isFinite("15") ); // true
alert( isFinite("str") ); // false,因为是一个特殊的值:NaN
alert( isFinite(Infinity) ); // false,因为是一个特殊的值:Infinity
有时 isFinite 被用于验证字符串值是否是常规数字
let num = +prompt("Enter a number", '');
// 结果会是 true,除非你输入的是 Infinity、-Infinity 或不是数字
alert( isFinite(num) );
**注意:**与 Object.is 进行比较
有一个特殊的内建方法 Object.is
,它类似于 ===
一样对值进行比较,但它对于两种边缘情况更可靠:
- 它适用于
NaN
:Object.is(NaN,NaN)=== true
,这是件好事。 - 值
0
和-0
是不同的:Object.is(0,-0)=== false
,从技术上讲这是对的,因为在内部,数字的符号位可能会不同,即使其他所有位均为零。
在所有其他情况下,Object.is(a,b)
与 a === b
相同。
这种比较方式经常被用在 JavaScript 规范中。当内部算法需要比较两个值是否完全相同时,它使用 Object.is
(内部称为 SameValue)。
parseInt 和 parseFloat
使用加号 +
或 Number()
的数字转换是严格的。如果一个值不完全是一个数字,就会失败:
alert( +"100px" ); // NaN
parseInt 返回一个整数,parseFloat 返回一个浮点数。
alert( parseInt('100px') ); // 100
alert( parseFloat('12.5em') ); // 12.5
alert( parseInt('12.3') ); // 12,只有整数部分被返回了
alert( parseFloat('12.3.4') ); // 12.3,在第二个点出停止了读取
而某些情况下会返回 NaN。当没有数字可读时会发生
alert( parseInt('a123') ); // NaN,第一个符号停止了读取
parseInt(str, radix)` 的第二个参数
parseInt()
函数具有可选的第二个参数。它指定了数字系统的基数,因此 parseInt
还可以解析十六进制数字、二进制数字等的字符串:
alert( parseInt('0xff', 16) ); // 255
alert( parseInt('ff', 16) ); // 255,没有 0x 仍然有效
alert( parseInt('2n9c', 36) ); // 123456
其它数学函数
Math.random()
返回一个从0到1的随机数(不包括1)
alert( Math.random() ); // 0.1234567894322
alert( Math.random() ); // 0.5435252343232
alert( Math.random() ); // ... (任何随机数)
Math.max(a,b,c…) / Math.min(a,b,c…)
从任意数量的参数中返回最大/最小值。
alert( Math.max(3, 5, -10, 0, 1) ); // 5
alert( Math.min(1, 2) ); // 1
Math.pow(n, power)
返回 n 的给定 (power) 次幂
alert( Math.pow(2, 10) ); // 2 的 10 次幂 = 1024