跳至主要内容

JavaScript从零开始——数据类型(2)

 前面那个部分介绍的比较简单,这样容易理解一点,接下来就可以来看看稍稍复杂一些的类型了:

1. 布尔值

只有true(真)和false(伪)两个固定值的数据类型,本能的让我想起计算机二进制里面的10,哈哈。

通常这个数据类型是用来对程序流程进行控制的,如果你还记得判断语句,就会发现一个判断语句可以执行下去的依据就是布尔值,如下:

// 无限循环
if (true) {
    // ...
}

所以在JavaScript预期是布尔值的地方(如if后面的括号里面),就会根据各种条件运算出对应的布尔值,包括:

  • 前置逻辑运算符:  (Not)
  • 相等运算符:=====!=!==
  • 比较运算符:>>=<<=

通过上面的这些运算符,就可以得到布尔值了;不过也有可能看到一种很特殊的情况,就是括号里面是一个非布尔值的变量,这种情况下,由于JavaScript预期会得到布尔值,就会进行强制的自动类型转换,把下面这6个值识别为false

  • undefined
  • null
  • false
  • 0
  • NaN
  • ""''(空字符串)

而除此以外的其他值自动识别为true。

如图,空字符串为false

在这个地方经常会有个误解,因为空字符串会被认为是伪,有些人会感觉空数组或者空对象应该也是伪,可事实上呢?

其实空数组或者空对象在自动转换的时候,会被识别为真(true)的,注意别踩这个坑!


2. 数值

2.1 整数和浮点数

和其他的语言有很大的区别,JavaScript是没有区分整数和浮点数的,在它内部,所有的数值都是使用64位的浮点数储存的,故而请注意11.0是全等的:

类似的运算在Python当中虽然也可以得到相同的结果,因为Python会自动将整数值转换为浮点数进行对比计算,但是两者并不全等,所以使用1 is 1.00 进行判断得到的结果是False(Python当中的布尔值首字母是大写的):

也就是说,JavaScript底层根本没有整数这个类型,所有的数字类型其实都是浮点数值。由于浮点数设计之初就不是个精确的数值,故而一定要留意带有小数点的浮点数相互计算的结果,譬如下面的这个经典问题:

别怀疑自己的眼睛,事实如此,我也很无奈……

2.2 数值精度

根据国际标准 IEEE 754,JavaScript 浮点数的64个二进制位,从最左边开始,是这样组成的。

  • 第1位:符号位,0表示正数,1表示负数
  • 第2位到第12位(共11位):指数部分
  • 第13位到第64位(共52位):小数部分(即有效数字)

符号位决定了一个数的正负,指数部分决定了数值的大小,小数部分决定了数值的精度。

指数部分一共有11个二进制位,因此大小范围就是0到2047。IEEE 754 规定,如果指数部分的值在0到2047之间(不含两个端点),那么有效数字的第一位默认总是1,不保存在64位浮点数之中。也就是说,有效数字这时总是1.xx...xxx的形式,其中xx...xxx的部分保存在64位浮点数之中,最长可能为52位。因此,JavaScript 提供的有效数字最长为53个二进制位

(-1)^符号位 * 1.xx...xx * 2^指数部分

上面公式是正常情况下(指数部分在0到2047之间),一个数在 JavaScript 内部实际的表示形式。

精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即 [公式] 到 [公式] ,都可以精确表示。

如上图第一到第三行,在 [公式] 范围以内,数值是可以正确表示出来的;可一旦超出,就不再准确了。由于2的53次方是一个16位的十进制数值,所以简单的法则就是,JavaScript 对15位的十进制数都可以精确处理。

如上图,超出的几位数字,是完全无法保存的……

2.3 数值范围

根据标准,64位浮点数的指数部分的长度是11个二进制位,意味着指数部分的最大值是2047(2的11次方减1)。也就是说,64位浮点数的指数部分的值最大为2047,分出一半表示负数,则 JavaScript 能够表示的数值范围为 [公式] 到 [公式] (开区间),超出这个范围的数无法表示。

如果一个数大于等于2的1024次方,那么就会发生“正向溢出”,即 JavaScript 无法表示这么大的数,这时就会返回Infinity;而如果一个数小于等于2的-1075次方(指数部分最小值-1023,再加上小数部分的52位),那么就会发生为“负向溢出”,即 JavaScript 无法表示这么小的数,这时会直接返回0

来看个实际的例子,我们声明名字为x变量,赋值为0.8,通过循环让它连续自乘25次:

由于最后结果太接近0,超出了可表示的范围,JavaScript 就直接将其转为0

JavaScript 提供Number对象的MAX_VALUEMIN_VALUE属性,返回可以表示的具体的最大值和最小值。

2.4 数值的进制和表示方式

JavaScript 对整数提供四种进制的表示方法:十进制、十六进制、八进制、二进制。

  • 十进制:没有前导0的数值。
  • 八进制:有前缀0o0O的数值。
  • 十六进制:有前缀0x0X的数值。
  • 二进制:有前缀0b0B的数值。

默认情况下,JavaScript 内部会自动将八进制、十六进制、二进制转为十进制,比如:

如果在这些进制的数值当中出现不正确的其他数值,就会报错。

  • 八进制数值不能包含数字8和9;
  • 十六进制数值不能包含排序在F(代表15)之后所有字母;
  • 二进制数值不能包含大于1的数字;

如下图:

以上这些都是用字面形式直接表示,JavaScript也可以支持使用科学计数法表示:

科学计数法允许字母eE的后面,跟着一个整数,表示这个数值的指数部分。

以下两种情况,JavaScript 会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示。

  1. 小数点前的数字多于21位;
  2. 小数点后的零多于5个;

如图:

2.5 几个特殊数值

  • 0

JavaScript 的64位浮点数之中,有一个二进制位是符号位。这意味着,任何一个数都有一个对应的负值,就连0也不例外。

JavaScript 内部实际上存在2个0:一个是+0,一个是-0,区别就是64位浮点数表示法的符号位不同,本质上来说它们是等价的。

仅有一种特殊的情况,两者会不同:

这是因为前者得到的结果是正无穷大(+Infinity),而后者是负无穷大(-Infinity),故而两者不全等。

  • Infinity

无穷大,即Infinity,用来表示两种场景。一种是一个正的数值太大,或一个负的数值太小,无法表示;另一种是非0数值除以0,所得到的结果(是的,开发编程的世界里,0有的时候也可能是分母)。

上图中0除以0得到的是NaN(即非数值,Not a Number)。

Infinity有正负之分,即正无穷大和负无穷大,分别代表大于一切数值和小于一切数值(不包括NaN),且两者并不相等:

Infinity的计算规则就是典型的数学意义上的运算规则:

    • 任何非0NaN且非无穷数值与之相加、相减、相乘,结果都是无穷(区分符号);
    • 任何非0NaN且非无穷数值除以无穷,结果都是0;反之则是无穷;
    • 0乘以无穷,结果为NaN;其他运算则同上;
    • 无穷减去或是除以无穷,结果为NaN
    • null在计算中会被转换为0,等同于0与无穷的计算规则;
    • undefined与无穷计算,结果都为NaN

如下各图:

  • NaN

上面频繁的看到这个奇怪的数值NaN,它是JavaScript 的特殊值,表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合,以及一些数学运算会出现错误的场合,尤其是0 / 0

NaN不等于任何数值,包括它本身,这也就意味着无法通过比较运算符来判断NaN

与任何数值的计算结果,都是NaN

由于数值正向溢出(overflow)、负向溢出(underflow)和被0除,JavaScript 都不报错,所以单纯的数学运算几乎没有可能抛出错误。


3. 数值相关的全局方法

3.1 parseInt()

parseInt()这个方法,主要是用于将字符串转换为整数,直接把需要转换的字符串(变量)作为方法的参数放入括号内即可:

parseInt('345'); // 345
let x = '2384723';
parseInt(x); // 2384723

内部处理方式:

  • 当字符串头部有空格的时候,会自动去除:
  • 如果待转换对象不是字符串,则会先转换为字符串,再转换为整数:
  • 在转换过程中,会按照字符串的字符顺序依次转换,一旦遇到无法转换为数字的字符,就会停止转换进程,直接返回已转换的所有数字:
  • 如果第一个就是无法转换的字符,则不进行任何转换动作,并返回NaN
  • 该方法只会按照十进制进行转换,遇到0x开头的十六进制数字,也会转换为十进制整数:
  • 对于所有以科学记数法表现的数值,或者会被自动转换为科学记数法的数值,该方法无法转换,并会出现奇怪的错误:
  • 该方法可以接受第二个参数,将其他进制的整数转换为十进制数值,但该参数只接受2-36之间的数值,超过该范围,结果为NaN
  • 如果第二个参数是0null以及undefined,则会直接忽略,使用默认的10作为参数:

使用该方法,最需要注意的,就是自动类型转换所造成的各种意料之外的结果。

3.2 parseFloat()

有了转换整数的方法,自然会有转换浮点数的方法,就是这个parseFloat()了。

parseFloat('3.1415926'); // 3.1415926
let x = '9.8875';
parseFloat(x); // 9.8875

内部处理方式:

  • 对字符串的处理和parseInt()一致:去除头部空格、先转换为字符串再转换为浮点数值、依照字符串字符次序处理等:
  • 会将空字符串处理为NaN
  • 如果字符串符合科学计数法,则会进行相应的转换:

3.3 isNaN()

该方法本意是用来对非数值NaN进行判断,但由于设计上会对类型进行自动转换,字符串在转换后也会变成NaN,故而有可能需要判断的变量是字符串而不是原始的NaN

同理,数组和对象的判断结果也会是true;不过空数组或只包含一个数值成员的数组,则会转换为数值从而结果为false

故而为了避免得到不想要的结果,最好先判断一下变量的基础类型。

建议一个更可靠的方法:利用NaN为唯一不等于自身的值的这个特点,进行判断。

function testNaN(value) {
    return value !== value;
}

3.4 isFinite()

该方法返回一个布尔值,表示某个值是否为正常的数值。

除了Infinity-InfinityNaNundefined这几个值,isFinite()对于其他的数值都会返回true

评论

此博客中的热门博文

Node.js从零开始——事件、系统和流

毕竟不是一个真正的教程,这里主要还是以普及和介绍为主,所以这一部分就是 Node.js 的其他部分介绍了,主要也就是事件触发、操作系统以及流的知识。 1 事件触发器 因为我们之前在浏览器中使用 JavaScript ,所以知道 JS 通过事件处理了许多用户的交互:鼠标的单击、键盘按钮的按下、对鼠标移动的反应等等。 在后端, Node.js 也提供了使用 events 模块 构建类似系统的选项。 具体上,此模块提供了 EventEmitter 类,用于处理事件。 使用以下代码进行初始化: const EventEmitter = require ( 'events' ); const eventEmitter = new EventEmitter (); 该对象公开了 on 和 emit 方法: emit 用于触发事件 on 用于添加回调函数(会在事件被触发时执行) 例如,创建 start 事件,并提供一个示例,通过记录到控制台进行交互: eventEmitter . on ( 'start' , () => { console . log ( '开始' ); }); 当运行以下代码时: eventEmitter . emit ( 'start' ); 事件处理函数会被触发,且获得控制台日志。 可以通过将参数作为额外参数传给 emit() 来将参数传给事件处理程序: eventEmitter . on ( 'start' , number => { console . log ( `开始 ${ number } ` ); }); eventEmitter . emit ( 'start' , 23 ); 多个参数: eventEmitter . on ( 'start' , ( start , end ) => { console . log ( `从 ${ start } 到 ${ end } ` ); }); eventEmitter . emit ( 'start' ,

Node.js从零开始——HTTP 服务器

其实 Node.js 最初的目的,就是实现一个完全可以由 JavaScript 来进行开发的服务器端,所以归根到底,它的后端能力之一就是实现一个 HTTP 服务器,这里我们来看看它。 1 搭建 HTTP 服务器 其实前面我们已经看过了一个例子,不过这里再来看一个 HTTP web 服务器的例子: const http = require ( 'http' ); const port = 3000 ; const server = http . createServer (( req , res ) => { res . statusCode = 200 ; res . setHeader ( 'Content-Type' , 'text/plain' ); res . end ( '你好世界\n' ); }) server . listen ( port , () => { console . log ( `服务器运行在 http:// ${ hostname } : ${ port } /` ); }); 简要分析一下: 这里引入了 ref=" http:// nodejs.cn/api/http.html ">http 模块:使用该模块来创建 HTTP 服务器 服务器被设置为在指定的 3000 端口上进行监听, 当服务器就绪时,则 listen 回调函数会被调用 传入的回调函数会在每次接收到请求时被执行, 每当接收到新的请求时, "http://nodejs.cn/api/http.html#http_event_request">request 事件 会被调用,并提供两个对象:一个请求( http.IncomingMessage 对象)和一个响应( http.ServerResponse 对象) request 提供了请求的详细信息, 通过它可以访问请求头和请求的数据, response 用于构造要返回给客户端的数据;在此示例中: res . statusCode = 200 ; 设置 status

Web API从零开始——SVG

SVG 是我基本没有用过的知识块,所以这里也是边分享边学习,尽量在我自己理解的基础上来分享。 1 概念 SVG 是一种基于 XML 语法的图像格式,全称是可缩放矢量图(Scalable Vector Graphics);其他图像格式都是基于像素处理的, SVG 则是属于对图像的形状描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。 SVG 文件可以直接插入网页,成为 DOM 的一部分,然后用 JavaScript 和 CSS 进行操作。 上面是 SVG 代码直接插入网页的例子。 SVG 代码也可以写在一个独立文件中,然后用 、 、 、 等标签插入网页: < img src = "circle.svg" > < object id = "object" data = "circle.svg" type = "image/svg+xml" ></</span> object > < embed id = "embed" src = "icon.svg" type = "image/svg+xml" > < iframe id = "iframe" src = "icon.svg" ></</span> iframe > CSS 也可以使用 SVG 文件: . logo { background : url ( icon.svg ); } SVG 文件还可以转为 BASE64 编码,然后作为 Data URI 写入网页: < img src = "data:image/svg+xml;base64,[data]" > 2 语法 2.1 标签 我们可以把 SVG 代码都放在顶层标签 之中,下面是一个例子: < svg width = "100%" height = "100%" >