3.1 数字
和其他编程语言不同,JavaScript 不区分整数值和浮点数值。JavaScript中 的所有数字均用浮点数值表示。JavaScript 釆用 IEEE 754标准定义的 64 位浮点格式表示数字,这意味着它能表示的最大值是± 1.7976931348623157 x 10^308,最小值是±5 x 10^(-324)。
按照 JavaScript 中的数字格式,能够表示的整数范围是从-9 007 199 254 740 992 ~ 9 007 199 254 740 992 (即-2^53 〜 2^53),包含边界值。如果使用了超过此范围的整数,则无法保证低位数字的精度。然而需要注意的是,JavaScript 中实际的操作(比如数组索引)则是基于32位整数。
当一个数字直接出现在 JavaScript 程序中,我们称之为数字直接量(numeric literal)。JavaScript 支持多种格式的数字直接量。注意,在任何数字直接量前添加负号(-)可以得到它们的负值。但负号是一元求反运算符,并不是数字直接量语法的组成部分。
3.1.1 整型直接量
在JavaScript程序中,用一个数字序列表示一个十进制整数。例如:
0
3
10000000
注
java程序员应该很熟悉这种格式,就像他们熟悉双精度(double)类型一样。在C和C++的所有现代实现中也都用到了双精度类型。
除了十进制的整型直接量,JavaScript 同样能识别十六进制(以16为基数)值。所谓十六进制的直接量是指以“Ox”或“0X”为前缀,其后跟随十六进制数串的直接量。十六进制值是0 ~ 9之间的数字和a(A) ~ f(F)之间的字母构成,a ~ f 的字母对应的表示数字 10 ~ 15。下面是十六进制整型直接量的例子:
Oxff // 15*16 + 15 = 255 (十进制)
OXCAFE911
尽管 ECMAScript 标准不支持八进制直接量,但 JavaScript 的某些实现可以允许采用八进 制(基数为8)形式表示整数。八进制直接量以数字 0 开始,其后跟随一个由 0~7 (包括 0 和 7)之间的数字组成的序列,例如:
0377 // 3*64 + 7*8 + 7 = 255 (十进制)
由于某些 JavaScript 的实现支持八进制直接量,而有些不支持,因此最好不要使用以 0 为前缀的整型直接量,毕竟我们也无法得知当当前 JavaScript 的实现是否支持八进制的解析。在 ECMAScript 6的严格模式下,八进制直接量是明令禁止的。
'use strict'
var hzh = 010;
console.log("hzh = " + hzh);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
e:\HMV\JavaScript\JavaScript.js:2
var hzh = 010;
^^^
SyntaxError: Octal literals are not allowed in strict mode.
at wrapSafe (internal/modules/cjs/loader.js:915:16)
at Module._compile (internal/modules/cjs/loader.js:963:27)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47
[Done] exited with code=1 in 0.17 seconds
3.1.2 浮点型直接量
浮点型直接量可以含有小数点,它们采用的是传统的实数写法。一个实数由整数部分、小数点和小数部分组成。
此外,还可以使用指数记数法表示浮点型直接量,即在实数后跟字母 e 或 E,后面再跟正负号,其后再加一个整型的指数。这种记数方法表示的数值,是由前面的实数乘以10的指数次幂。
可以使用更简洁的语法表示:
[digits][.digits][(E|e)[(+|-)]digits]
例如:
3.14
2345.789
.333333333333333333
6.02e23 // 6.02 x 10^23
1.4738223E-32 // 1.4738223 x 10^(-32)
3.1.3 JavaScript中的算术运算
JavaScript 程序是使用语言本身提供的算术运算符来进行数字运算的。这些运算符包括加法运算符(+)、减法运算符(-)、乘法运算符(
*
)、除法运算符(/)和求余(求整除后的余数)运算符(%)。
除了基本的运算符外,JavaScript还支持更加复杂的算术运算,这些复杂运算通过作为 Math对象的属性定义的函数和常量来实现:
Math.pow(2,53) // => 9007199254740992: 2的 53次幂
Math.round(.6) // => 1.0: 四舍五入
Math.ceil(.6) // => 1.0: 向上求整
Math.floor(.6) // => 0.0: 向下求整
Math.abs(-5) // => 5: 求绝对值
Math.max(x,y,z) //返回最大值
Math.min(x.,y,z) //返回最小值
Math.random() //生成一个大于等于0小于1.0的伪随机数
Math.PI //π:圆周率
Math.E //e:自然对数的底数
Math.sqrt(3) //3的平方根
Math.pow(3, 1/3) //3的立方根
Math.sin(0) //三角函数:还有Math.cos, Math.atan等
Math.log(1O) //10的自然数
Math.log(100)/Math.LN10 //以10为底100的对数
Math.log(512)/Math.LN2 //以2为底512的对数
Math.exp(3) //e的三次幂
console.log("幂运算:");
console.log(Math.pow(2,53));
console.log("");
console.log("四舍五入:");
console.log(Math.round(.6));
console.log("");
console.log("向上求整:");
console.log(Math.ceil(.6) );
console.log("");
console.log("向下求整:");
console.log(Math.floor(.6));
console.log("");
console.log("求绝对值:");
console.log(Math.abs(-5));
console.log("");
var x = 1;
var y = 9;
var z = 2;
console.log("返回最大值:");
console.log(Math.max(x,y,z));
console.log("返回最小值:");
console.log(Math.min(x,y,z));
console.log("");
console.log("生成一个大于等于0小于1.0的伪随机数:");
console.log(Math.random());
console.log("");
console.log("圆周率:");
console.log(Math.PI);
console.log("");
console.log("自然对数的底数:");
console.log(Math.E);
console.log("");
console.log("3的平方根:");
console.log(Math.sqrt(3));
console.log("");
console.log("3的立方根:");
console.log(Math.pow(3, 1/3));
console.log("");
console.log("三角函数:");
console.log(Math.sin(0));
console.log("");
console.log("10的自然数:");
console.log(Math.log(10));
console.log("");
console.log("以10为底100的对数:");
console.log(Math.log(100)/Math.LN10);
console.log("");
console.log("以2为底512的对数:");
console.log(Math.log(512)/Math.LN2);
console.log("");
console.log("e的三次幂:");
console.log(Math.exp(3));
[Running] node "e:\HMV\JavaScript\JavaScript.js"
幂运算:
9007199254740992
四舍五入:
1
向上求整:
1
向下求整:
0
求绝对值:
5
返回最大值:
9
返回最小值:
1
生成一个大于等于0小于1.0的伪随机数:
0.049671088042935185
圆周率:
3.141592653589793
自然对数的底数:
2.718281828459045
3的平方根:
1.7320508075688772
3的立方根:
1.4422495703074083
三角函数:
0
10的自然数:
2.302585092994046
以10为底100的对数:
2
以2为底512的对数:
9
e的三次幂:
20.085536923187668
[Done] exited with code=0 in 0.265 seconds
JavaScript 中的算术运算在溢出(overflow),下溢(underflow)或被零整除时不会报错。当数字运算结果超过了JavaScript 所能表示的数字上限(溢出),结果为一个特殊的无穷大(infinity)值,在JavaScript 中以 Infinity 表示。同样地,当负数的值超过了 JavaScript 所能表示的负数范围,结果为负无穷大,在 JavaScript 中以 -Infinity 表示。无穷大值的行为特性和我们所期望的是一致的:基于它们的加、减、乘和除运算结果还是无穷大值(当然还保留它们的正负号)。
下溢(underflow)是当运算结果无限接近于零并比 JavaScript 能表示的最小值还小的时候发生的一种情形。这种情况下,JavaScript 将会返回 0。当一个负数发生下溢时,JavaScript 返回一个特殊的值“负零”。这个值(负零)几乎和正常的零完全一样,JavaScript 程序员很少用到负零。
被零整除在 JavaScript 并不报错:它只是简单的返回无穷大(infinity)或负无穷大(-Infinity)。但有一个例外,零除以零是没有意义的,这种整除运算结果也是一个非数字(not-a-number)值,用 NaN 表示。无穷大除以无穷大、给任意负数作开方运算或者算术运算符与不是数字或无法转换为数字的操作数一起使用时都将返回 NaN。
JavaScript 预定义了全局变量 Infinity 和 NaN,用来表示正无穷大和非数字值。在 ECMAScript 3中,这两个值是可读/写的,并可修改。ECMAScript 5 修正了这个错误,将它们定义为只读的。在ECMAScript 3 中 Number 对象定义的属性值也是只读的。这里有一些例子:
Infinity //将一个可读/写的变量初始化为infinity
Number.POSITIVE_INFINITY //同样的值,只读
Number.MAX_VALUE+1 //计算结果还是Infinity
Number.NEGATIVE_INFINITY //该表达式表示了负无穷大
-Infinity
-1/0
-Number.MAXVALUE-1
NaN //将一个可读/写的变量初始化为NaN
Number.NaN //同样的值,但是只读
0/0 //计算结果是NaN
Number.MIN VALUE/2 //发生下溢:计算结果为0
-Number.MIN_VALUE/2 //负零
-1/Infinity //同样是负零
-0
console.log("Infinity :" + Infinity);
console.log("");
console.log("Number.POSITIVE_INFINITY :" + Number.POSITIVE_INFINITY);
console.log("");
console.log("Number.MAX_VALUE+1: " + Number.MAX_VALUE+1);
console.log("");
console.log("Number.NEGATIVE_INFINITY:" + Number.NEGATIVE_INFINITY);
console.log("");
console.log("-Infinity:" + -Infinity);
console.log("");
console.log("-1/0:" + -1/0);
console.log("");
console.log("-Number.MAXVALUE-1:" + (-Number.MAXVALUE-1));
console.log("");
console.log("NaN:" + NaN);
console.log("");
console.log("Number.NaN:" + Number.NaN);
console.log("");
console.log("0/0:" + 0/0);
console.log("");
console.log("Number.MIN_VALUE/2:" + Number.MIN_VALUE/2);
console.log("");
console.log("-Number.MIN_VALUE/2:" +-Number.MIN_VALUE/2);
console.log("");
console.log("-1/Infinity:" + -1/Infinity);
console.log("");
console.log("-0:" + -0);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
Infinity :Infinity
Number.POSITIVE_INFINITY :Infinity
Number.MAX_VALUE+1: 1.7976931348623157e+3081
Number.NEGATIVE_INFINITY:-Infinity
-Infinity:-Infinity
-1/0:-Infinity
-Number.MAXVALUE-1:NaN
NaN:NaN
Number.NaN:NaN
0/0:NaN
Number.MIN_VALUE/2:0
-Number.MIN_VALUE/2:0
-1/Infinity:0
-0:0
[Done] exited with code=0 in 0.177 seconds
JavaScript 中的非数字值有一点特殊:它和任何值都不相等,包括自身。也就是说,没办法通过 x==NaN 来判断变量 x 是否是 NaN。
var x = NaN;
console.log("判断变量 x 是否是 NaN?");
console.log(x == NaN);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
判断变量 x 是否是 NaN?
false
[Done] exited with code=0 in 0.718 seconds
相反,应当使用 x!=x 来判断,当且仅当 x 为 NaN 的时候,表达式的结果才为 true。
var x = NaN;
console.log("判断变量 x 是否是 NaN?");
console.log(x != x);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
判断变量 x 是否是 NaN?
true
[Done] exited with code=0 in 0.458 seconds
函数 isNaN() 的作用与此类似,如果参数是 NaN 或者是一个非数字值(比如字符串和对象),则返回 true。
var x = NaN;
console.log("判断变量 x 是否是 NaN?");
console.log(isNaN(x));
[Running] node "e:\HMV\JavaScript\JavaScript.js"
判断变量 x 是否是 NaN?
true
[Done] exited with code=0 in 1.414 seconds
JavaScript 中有一个类似的函数 isFinite(),在参数不是 NaN、Infinity 或 -Infinity 的时候返回 true。
console.log("在参数不是 NaN、Infinity 或 -Infinity 的时候返回 true:");
var hzh1 = NaN;
var hzh2 = -Infinity;
var hzh3 = Infinity;
var hzh4 = 19124896017;
console.log("NaN:" + isFinite(hzh1));
console.log("-Infinity:" + isFinite(hzh2));
console.log("Infinity:" + isFinite(hzh3));
console.log("19124896017:" + isFinite(hzh4));
[Running] node "e:\HMV\JavaScript\JavaScript.js"
在参数不是 NaN、Infinity 或 -Infinity 的时候返回 true:
NaN:false
-Infinity:false
Infinity:false
19124896017:true
[Done] exited with code=0 in 0.479 seconds
负零值同样有些特殊,它和正零值是相等的(甚至使用 JavaScript 的严格相等测试来判断)。这意味着这两个值几乎一模一样,除了作为除数之外:
var zero = 0; //正常的零值
var negz = -0; //负零值
zero === negz // => true: 正零值和负零值相等
1/zero === 1/negz // => false: 正无穷大和负无穷大不等
var zero = 0;
var negz = -0;
console.log("判断zero 和 negz 是否相等:");
console.log(zero == negz );
console.log("");
console.log("判断zero 和 negz 是否严格相等:");
console.log(zero === negz );
console.log("");
console.log("判断1/zero 和 1/negz 是否相等:");
console.log((1/zero) == (1/negz) );
console.log("");
console.log("判断1/zero 和 1/negz 是否严格相等:");
console.log((1/zero) === (1/negz) );
[Running] node "e:\HMV\JavaScript\JavaScript.js"
判断zero 和 negz 是否相等:
true
判断zero 和 negz 是否严格相等:
true
判断1/zero 和 1/negz 是否相等:
false
判断1/zero 和 1/negz 是否严格相等:
false
[Done] exited with code=0 in 0.188 seconds
3.1.4 二进制浮点数和四舍五入错误
实数有无数个,但 JavaScript 通过浮点数的形式只能表示其中有限的个数(确切地说是 18 437 736 874 454 810 627个)。也就是说,当在 JavaScript 中使用实数的时候,常常只是真实值的一个近似表示。
JavaScript 采用了 IEEE-754 浮点数表示法(几乎所有现代编程语言所采用),这是一种二进制表示法,可以精确地表示分数,比如 1/2、1/8 和 1/1024。遗憾的是,我们常用的分数(特别是在金融计算方面)都是十进制分数 1/10、1/100 等。二进制浮点数表示法并不能精确表示类似 0.1 这样简单的数字。
JavaScript 中的数字具有足够的精度,并可以极其近似于 0.1。但事实是,数字不能精确表述的确带来了一些问题。看下这段代码:
var x = .3 - .2; // 30美分减去20美分
var y = .2 - .1; // 20美分减去10美分
x == y // => false:两值不相等!
x == .1 // => false: .3 - .2 不等于.1
y == .1 // => true: .2 - .1 等于.1
var x = .3 - .2;
var y = .2 - .1;
console.log("判断x和y是否相等?");
console.log(x == y);
console.log("判断x和.1是否相等?");
console.log(x == .1);
console.log("判断y和.1是否相等?");
console.log(y == .1);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
判断x和y是否相等?
false
判断x和.1是否相等?
false
判断y和.1是否相等?
true
[Done] exited with code=0 in 0.24 seconds
由于舍入误差,0.3 和 0.2 之间的近似差值实际上并不等于 0.2 和 0.1 之间的近似差值。这个问题并不只在 JavaScript 中才会出现,理解这一点非常重要:在任何使用二进制浮点数的编程语言中都会有这个问题。同样需要注意的是,上述代码中 x 和 y 的值非常接近彼此和最终的正确值。这种计算结果可以胜任大多数的计算任务:这个问题也只有在比较两个值是否相等的时候才会出现。
注
在JavaScript的真实运行环境中,0.3-0.2=0.099 999 999 999 999 98
var hzh2 = 0.3;
var hzh1 = 0.2;
console.log("输出0.3-0.2的结果:");
console.log(hzh2-hzh1);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
输出0.3-0.2的结果:
0.09999999999999998
[Done] exited with code=0 in 0.603 seconds
JavaScript 的未来版本或许会支持十进制数字类型以避免这些舍入问题。在这之前你可能更愿意使用大整数进行重要的金融计算,例如,要使用整数“分”而不要使用小数“元”进行基于货币单位的运算。
3.1.5 日期和时间
JavaScript 语言核心包括 Date() 构造函数,用来创建表示日期和时间的对象。这些日期对象的方法为日期计算提供了简单的 API。日期对象不像数字那样是基本数据类型。这里给出了使用日期对象的一个简单教程:
var then = new Date(2011, 0, 1); // 2011年1月1日
var later = new Date(2011, 0, 1, 17, 10, 30); // 同一天,当地时间5:10:30pm,
var now = new Date(); // 当前日如和时间
var elapsed = now - then; // 日期减法:计算时间间隔的毫秒数
later.getFullYear() // => 2011
later.getMonth() // => 0: 从0开始计数的月份
later.getDate() // => 1: 从1开始计数的天数
later.getDay() // => 5: 得到星期几,0代表星期日,5代表星期一
later.getHours() // => 当地时间 17: 5pm
later.getUTCHours() // 使用UTC表示小时的时间,基于时区
var then = new Date(2011, 0, 1); // 2011年1月1日
var later = new Date(2011, 0, 1, 17, 10, 30); // 同一天,当地时间5:10:30pm,
var now = new Date(); // 当前日如和时间
var elapsed = now - then; // 日期减法:计算时间间隔的毫秒数
console.log("输出一个特定的时间:");
console.log(then);
console.log("");
console.log("输出一个详细的时间:");
console.log(later);
console.log("");
console.log("输出一个当前的时间:");
console.log(now);
console.log("");
console.log("输出时间差:");
console.log(elapsed);
console.log("");
console.log("获取年份:");
console.log(later.getFullYear());
console.log("");
console.log("获取月份(从0开始计算月份):");
console.log(later.getMonth());
console.log("");
console.log("获取天数(从1开始计数的天数):");
console.log(later.getDate());
console.log("");
console.log("得到星期几,0代表星期日,5代表星期一:");
console.log(later.getDay());
console.log("");
console.log("获取小时(24小时):");
console.log(later.getHours());
console.log("");
console.log("使用UTC表示小时的时间,基于时区:");
console.log(later.getUTCHours());
[Running] node "e:\HMV\JavaScript\JavaScript.js"
输出一个特定的时间:
2010-12-31T16:00:00.000Z
输出一个详细的时间:
2011-01-01T09:10:30.000Z
输出一个当前的时间:
2022-05-24T03:11:23.552Z
输出时间差:
359550683552
获取年份:
2011
获取月份:
0
获取日期:
1
获取天数:
6
获取小时:
17
获取:
9
[Done] exited with code=0 in 0.302 seconds