9月阅读周·JavaScript权威指南:类型、值和变量,数字篇
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读八个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》。
当前阅读周书籍:《JavaScript权威指南》。
数字
按照JavaScript中的数字格式,能够表示的整数范围是从-9007199254740992~9007199254740992(即-253~253),包含边界值。如果使用了超过此范围的整数,则无法保证低位数字的精度。然而需要注意的是,JavaScript中实际的操作(比如数组索引,以及第4章讲到的位操作符)则是基于32位整数。
当一个数字直接出现在JavaScript程序中,我们称之为数字直接量(numeric literal)。JavaScript支持多种格式的数字直接量,在接下来的小节中会有讨论。注意,在任何数字直接量前添加负号(-)可以得到它们的负值。但负号是一元求反运算符(参见第4章),并不是数字直接量语法的组成部分。
整型直接量
在JavaScript程序中,用一个数字序列表示一个十进制整数。例如:
0
3
10000000
除了十进制的整型直接量,JavaScript同样能识别十六进制(以16为基数)值。所谓十六进制的直接量是指以"0x"或"0X"为前缀,其后跟随十六进制数串的直接量。十六进制值是0~9之间的数字和a(A)~f(F)之间的字母构成,a~f的字母对应的表示数字10~15。下面是十六进制整型直接量的例子:
0xff//15*16+15=255(十进制)
0xCAFE911
尽管ECMAScript标准不支持八进制直接量,但JavaScript的某些实现可以允许采用八进制(基数为8)形式表示整数。八进制直接量以数字0开始,其后跟随一个由0~7(包括0和7)之间的数字组成的序列,例如:
0377//3*64+7*8+7=255(十进制)
由于某些JavaScript的实现支持八进制直接量,而有些不支持,因此最好不要使用以0为前缀的整型直接量,毕竟我们也无法得知当前JavaScript的实现是否支持八进制的解析。在ECMAScript 6(见5.7.3节)的严格模式下,八进制直接量是明令禁止的。
浮点型直接量
浮点型直接量可以含有小数点,它们采用的是传统的实数写法。一个实数由整数部分、小数点和小数部分组成。
此外,还可以使用指数记数法表示浮点型直接量,即在实数后跟字母e或E,后面再跟正负号,其后再加一个整型的指数。这种记数方法表示的数值,是由前面的实数乘以10的指数次幂。
可以使用更简洁的语法表示:
[digits][.digits][(E|e)[(+|-)]digits]
例如:
3.14
2345.789
.333333333333333333
6.02e23//6.02×1023
1.4738223E-32//1.4738223×10-32
JavaScript中的算术运算
JavaScript程序是使用语言本身提供的算术运算符来进行数字运算的。这些运算符包括加法运算符(+)、减法运算符(-)、乘法运算符(*)、除法运算符(/)和求余(求整除后的余数)运算符(%)。第4章将详细介绍这些以及更多的运算符。
除了基本的运算符外,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(10)//10的自然对数
Math.log(100)/Math.LN10//以10为底100的对数
Math.log(512)/Math.LN2//以2为底512的对数
Math.exp(3)//e的三次幂
参阅第三部分中关于Math对象的介绍,那里列出了JavaScript所支持的所有数学函数。
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对象定义的属性值也是只读的。这里有一些例子:
JavaScript预定义了全局变量Infinity和NaN,用来表示正无穷大和非数字值。在ECMAScript 3中,这两个值是可读/写的,并可修改。ECMAScript 5修正了这个错误,将它们定义为只读的。在ECMAScript 3中Number对象定义的属性值也是只读的。这里有一些例子:
JavaScript中的非数字值有一点特殊:它和任何值都不相等,包括自身。也就是说,没办法通过x==NaN来判断变量x是否是NaN。相反,应当使用x!=x来判断,当且仅当x为NaN的时候,表达式的结果才为true。函数isNaN()的作用与此类似,如果参数是NaN或者是一个非数字值(比如字符串和对象),则返回true。JavaScript中有一个类似的函数isFinite(),在参数不是NaN、Infinity或-Infinity的时候返回true。
负零值同样有些特殊,它和正零值是相等的(甚至使用JavaScript的严格相等测试来判断)。这意味着这两个值几乎一模一样,除了作为除数之外:
var zero=0;//正常的零值
var negz=-0;//负零值
zero===negz//=>true:正零值和负零值相等
1/zero===1/negz//=>false:正无穷大和负无穷大不等
二进制浮点数和四舍五入错误
实数有无数个,但JavaScript通过浮点数的形式只能表示其中有限的个数(确切地说是18437736874454810627个)。也就是说,当在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
由于舍入误差,0.3和0.2之间的近似差值实际上并不等于0.2和0.1之间的近似差值[插图]。这个问题并不只在JavaScript中才会出现,理解这一点非常重要:在任何使用二进制浮点数的编程语言中都会有这个问题。同样需要注意的是,上述代码中x和y的值非常接近彼此和最终的正确值。这种计算结果可以胜任大多数的计算任务:这个问题也只有在比较两个值是否相等的时候才会出现。
日期和时间
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表示小时的时间,基于时区
总结
JavaScript变量是无类型的(untyped),变量可以被赋予任何类型的值,同样一个变量也可以重新赋予不同类型的值。使用var关键字来声明(declare)变量。JavaScript采用词法作用域(lexical scoping)。不在任何函数内声明的变量称做全局变量(global variable),它在JavaScript程序中的任何地方都是可见的。在函数内声明的变量具有函数作用域(function scope),并且只在函数内可见。
和其他编程语言不同,JavaScript不区分整数值和浮点数值。JavaScript中的所有数字均用浮点数值表示。JavaScript采用IEEE 754标准定义的64位浮点格式表示数字,这意味着它能表示的最大值是±1.7976931348623157×10308,最小值是±5×10-324。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)