【JavaScript 04】数据类型 typeof运算符 null与undefined 布尔值 数值 整数 浮点数 数值精度 数值范围 数值表示 进制 特殊数值 数值相关的全局方法

数据类型

JS的每一个值都是属于某种数据类型,总共6种
但是ES6新增第7种Symbol类型的值

  1. number 整数和小数
  2. string 文本
  3. boolean 表示真伪
  4. undefined 表示未定义or不存在
  5. null 表示空值
  6. object 各种值组成的集合

数值,字符串,布尔值是为原始类型(primitive type)
对象是为合成类型(complex type)
而undefined和null是两个特殊类型

对象是复杂的,可以细分为三种子类型

  1. 狭义对象 object
  2. 数组 array
  3. 函数 function

狭义的对象和数组是两种不同的数据组合方式
函数是处理数据的方法,JavaScript 当成一种数据类型,从而可以赋值给变量,这为编程带来了很大的灵活性,也为 JavaScript 的函数式编程奠定了基础

一般对象是指狭义对象


typeof 运算符

JS共有三种方法用于确定值类型

  1. typeof 运算符
  2. instanceof 运算符
  3. Object.prototype.toString 方法

typeof运算符可以返回值的数据类型

typeof 123 // 'number' 这里不同浏览器的引号单双不同,属于无关紧要
typeof '123' // 'string'
typeof false // 'boolean'

function f() {}
typeof f
// "function"

typeof window // "object"
typeof {} // "object"
typeof [] // "object"

typeof undefined
// "undefined"
// 利用这一点,typeof可以用来检查一个没有声明的变量,而不报错

v
// ReferenceError: v is not defined

typeof v
// "undefined"
// 上面代码中,变量v没有用var命令声明,直接使用就会报错
// 但是,放在typeof后面,就不报错了,而是返回undefined,实际编程中我们通常用在条件语句中

// 错误的写法
if (v) {
  // ...
}
// ReferenceError: v is not defined

// 正确的写法
if (typeof v === "undefined") {
  // ...
}

上面代码中,空数组([])的类型也是object,这表示在 JavaScript 内部中数组本质上只是一种特殊的对象
instanceof 运算符可以区分数组和对象

var o = {};
var a = [];

o instanceof Array // false
a instanceof Array // true

null 则会返回 object

typeof null // "object"

null的类型是object是由于历史原因造成的
1995年的 JavaScript 语言第一版设计了五种数据类型(对象、整数、浮点数、字符串和布尔值),没考虑null,只把它当作object的一种特殊值
后来null独立出来,作为一种单独的数据类型,为了兼容以前的代码,typeof null返回object没法改变


null and undefined

null与undefined含义类似
很多情况下二者的语法效果没有区别

// 变量赋值
var a = null;
var a = undefined;

// 在if语句中都自动转为false
if (!null) {
  console.log('null is false');
}
// null is false

if (!undefined) {
	console.log('undefined is false');
}
// undefined is false

// 同时相等运算符==直接报告二者相等
null == undefined
// true
// 但要注意若是严格相等运算符则是false

谷歌公司开发的 JavaScript 语言的替代品 Dart 语言就明确规定只有null而没有undefined

究其根本原因还是由于历史,和typeof null返回object有异曲同工之"妙"

1995年 JavaScript 诞生时,最初像 Java 一样,只设置了null表示"无",同C语言的传统,null可以自动转换为0

Number(null) // 0
5 + null // 5

Brendan Eich设计的第一版 JavaScript 里面的null就像在 Java 里一样被当成一个对象
他觉得"无"最好不是对象,同时当时的JS没有错误处理机制,null自动转换为0不容易发现

于是他设计undefined,null表示空对象,可以转为数值0,而undefined表示无定义,转为数值则变成NaN

Number(undefined) // NaN
5 + undefined // NaN

null表示空值即该处的值现在为空
调用函数时某个参数未设置任何值,这时就可以传入null,表示该参数为空
比如,某个函数接受引擎抛出的错误作为参数,如果运行过程中未出错,那么这个参数就会传入null,表示未发生错误

undefined表示未定义
有很多返回undefined的典型场景

// 变量声明但未赋值
var i;
i // undefined

// 调用函数时应当提供的参数没有提供,则参数等于undefined
function f(x) {
	return x;
}
f() // undefined

// 对象没有赋值的属性
var o = new Object();
o.p // undefined

// 函数没有返回值时,默认返回 undefined
function f() {}
f() // undefined

布尔值

代表真假两个状态,真为true,假为false

下面的算符将会返回布尔值:
- 前置逻辑运算符:! Not
- 相等运算符: ===, !==, ==, !=
- 比较运算符: >, >=, <, <=

若JS预期某处需要是布尔值,则会将该处的值自动转换为布尔值
规则是以下6个值转为false,其余全部转为true

  1. undefined
  2. null
  3. false
  4. 0
  5. NaN
  6. “” or ‘’ (空串)

布尔值常用于程序流程控制

if ('') {
	console.log('true');
}
// 无任何输出

if命令后面的判断条件,预期应该是一个布尔值,所以 JavaScript 自动将空字符串,转为布尔值false,导致程序不会进入代码块,所以没有任何输出

但是需要注意的是空数组和空对象对应的布尔值都是true

if ([]) {
  console.log('true');
}
// true

if ({}) {
  console.log('true');
}
// true

数值

整数与浮点数

JS内部的所有数字(包括整数)都是通过64位浮点数形式存储
1与1.0是同一个数

1 === 1.0
// true

JS的底层根本没有整数,所有数字都是小数(64位浮点数)
但是有些运算只有整数才能完成,这是JS会自动将64位浮点数转换为32位整数后进行运算

由于浮点数不是精确的数,因此有关小数的比较和运算需要非常小心

0.1 + 0.2 === 0.3
// false

0.3 / 0.1
// 2.9999999999999996
// 2.9999999999999996,所有浏览器都是这个数

(0.3 - 0.2) === (0.2 - 0.1)
// false
数值精度

由国际标准 IEEE 754规定
JS浮点数的64个二进制位从最左开始这样组成:

  • 第1位:符号位,0正1负,决定了一个数的正负
  • 第2位至第12位(11位):指数部分,决定了数值的大小
  • 第13位至第64位(52位):小数部分(有效数字),决定了数值的精度

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

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

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

精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即 − 2 53 到 2 53 -2^{53} 到 2^{53} 253253,都可以精确表示

Math.pow(2, 53)
// 9007199254740992

Math.pow(2, 53) + 1
// 9007199254740992

Math.pow(2, 53) + 2
// 9007199254740994

Math.pow(2, 53) + 3
// 9007199254740996

Math.pow(2, 53) + 4
// 9007199254740996

上面代码中大于2的53次方以后,整数运算的结果开始出现错误
由此可见大于2的53次方的数值都无法保持精度

由于2的53次方是一个16位的十进制数值,所以简单的法则就是JavaScript 对15位的十进制数全都可以精确处理

Math.pow(2, 53)
// 9007199254740992

// 多出的三个有效数字,将无法保存变成000
9007199254740992111
// 9007199254740992000
数值范围

64位浮点数的指数部分的长度是11个二进制位,意味着指数部分的最大值是2047(2的11次方减1)
也即64位浮点数的指数部分值最大是2047,并且需要分出一半表示负数
则JS指数部分能够表示的数值范围是 2 1024 2^{1024} 21024 2 − 1023 2^{-1023} 21023开区间,超过此范围的数值将无法表示

若一个数值大于等于2的1024次方,则会发生正向溢出(overflow),JS无法表示,直接返回 Infinity
若一个数值小于等于2的-1075次方(指数部分最小值-1023,加之小数部分的52位),则会发生负向溢出(underflow),JS也无法表示,直接返回 0

Math.pow(2, 1024) // Infinity
Math.pow(2, -1075) // 0

var x = 0.5;

for(var i = 0; i < 25; i++) {
  x = x * x;
}

x // 0

上面代码中,对0.5连续做25次平方,由于最后结果太接近0,超出了可表示的范围,JavaScript 就直接将其转为0

JS提供 Number对象的 MAX_VALUE 和 MIN_VALUE属性返回可以表示的具体的最大和最小值

Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324

数值表示

JS的数值表示方法有多种,可以直接使用字面量,如35(十进制)和0xFF(十六进制)
也可以使用科学计数法

123e3 // 123000
123e-3 // 0.123
-3.1E+12
.1e-23

科学计数法允许字母e或E的后面跟着一个整数,用于表示这个数值的指数部分
以下两种情况,JavaScript 会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示

  1. 小数点前的数字多于21位
  2. 小数点后的0多于5个
1234567890123456789012
// 1.2345678901234568e+21

123456789012345678901
// 123456789012345680000

// 小数点后紧跟5个以上的零,
// 就自动转为科学计数法
0.0000003 // 3e-7

// 否则,就保持原来的字面形式
0.000003 // 0.000003
进制

使用字面量(literal)直接表示数值时,JS对整数提供了四种进制的表示方法
分别是:十进制,二进制,八进制以及十六进制

  • 十进制没有前缀
  • 二进制有前缀0b or 0B
  • 八进制有前缀0o or 00,或者使有前导0且只用到0-7的8个阿拉伯数字的数值
  • 十六进制有前缀0x or 0X

默认情况下JS内部自动将其他进制转换为十进制

0xff // 255
0o88 // 报错
0b22 // 报错

// 通常来说,有前导0的数值会被视为八进制,但是如果前导0后面有数字8和9,则该数值被视为十进制
0888 // 888
0777 // 511

由此可见前缀0可能会造成混乱,因此ES5的严格模式以及ES6已经废除这种表示法
但是浏览器为了兼容之前的代码还继续支持

特殊数值

正0与负0
JS的64位浮点数中有一个二进制符号位,这意味着任何数(包括0)都有对应的负值
JS内部实际存在2个0,在绝大多数场合(除了分母)二者等价于正常的0

-0 === +0 // true
0 === -0 // true
0 === +0 // true

+0 // 0
-0 // 0
(-0).toString() // '0'
(+0).toString() // '0'

// 唯一有区别的是当二者作为分母时返回+Infinity和-Infinity
(1 / +0) === (1 / -0) // false

NaN
NaN是JS的特殊数值,表示Not a Number
通常出现在字符串解析错误或者非法数学运算返回结果

// 代码运行时,会自动将字符串x转为数值,但是由于x不是数值,所以最后得到结果为NaN,表示它是“非数字”(NaN)
5 - 'x' // NaN

Math.acos(2) // NaN
Math.log(-1) // NaN
Math.sqrt(-1) // NaN
0 / 0 // NaN

// 需要注意的是,NaN不是独立的数据类型,而是一个特殊数值,它的数据类型依然属于Number,使用typeof运算符可以看得很清楚
typeof NaN // 'number'

NaN不等于任何值,包括其本身
数组的 indexOf 方法内部使用严格相等运算符,因此对NaN不成立(实际上相等运算符也不成立)
前面也说过,NaN参与布尔运算会转为false
NaN与任何数(包括本身)的运算都会得到NaN

NaN === NaN // false
[NaN].indexOf(NaN) // -1
Boolean(NaN) // false

NaN + 32 // NaN
NaN - 32 // NaN
NaN * 32 // NaN
NaN / 32 // NaN

Infinity
无穷,需要注意首字母需要大写
在这里插入图片描述

Math.pow(2, 1024)
// Infinity

// 场景二
0 / 0 // NaN
1 / 0 // Infinity

同时 Infinity 有正负之分

Infinity === -Infinity // false

1 / -0 // -Infinity
-1 / -0 // Infinity

由于数值正向溢出(overflow)负向溢出(underflow)被0除JavaScript 都不报错,所以单纯的数学运算几乎没有可能抛出错误
确实是这样的,目前我们学习到现在报错的只有数值的进制表示错误才会报错,其他情况都没有报错

Infinity大于一切数值(除了NaN),-Infinity小于一切数值(除了NaN)
Infinity与NaN比较,总是返回false

Infinity > 1000 // true
-Infinity < -1000 // true

Infinity > NaN // false
Infinity < NaN // false

-Infinity > NaN // false
-Infinity < NaN // false

而Infinity的四则运算符合无穷的数学计算
需要注意的是0与Infinity的乘积以及Infinity与自身的减和除都是NaN
Infinity与null计算时,null会自动转成0,等同于与0的计算
Infinity与undefined计算,undefined会自动转成NaN,等同于与NaN的计算,返回的都是NaN

5 * Infinity // Infinity
5 - Infinity // -Infinity
Infinity / 5 // Infinity
5 / Infinity // 0

0 * Infinity // NaN
0 / Infinity // 0
Infinity / 0 // Infinity

Infinity + Infinity // Infinity
Infinity * Infinity // Infinity

Infinity - Infinity // NaN
Infinity / Infinity // NaN

null * Infinity // NaN
null / Infinity // 0
Infinity / null // Infinity

数值相关的全局方法

parseInt()

1 基本用法
parseInt 方法用于将字符串转为整数
若串头部有空格则会自动去除(中间有空格则按照无法转换处理)
若参数不是字符串则会先转为字符串再进行转为整数
可参考String 方法
在这里插入图片描述

parseInt('1234') // 1234
parseInt('   666') // 666
parseInt(1.23) // 1
parseInt([1.23]) // 1
parseInt([1.23, 3.45]) // 1
// 等同于
parseInt('1.23') // 1

字符串转为整数的时候,是一个个字符依次转换,如果遇到不能转为数字的字符,就不再进行下去,返回已经转好的部分
字符串的第一个字符不能转换为数字(后面跟着数字的正负号除外),返回NaN

parseInt('8a') // 8
parseInt('12**') // 12
parseInt('12.34') // 12
parseInt('15e2') // 15
parseInt('15px') // 15

parseInt('abc') // NaN
parseInt('.3') // NaN
parseInt('') // NaN
parseInt('+') // NaN
parseInt('+1') // 1

如果字符串以0x或0X开头,parseInt会将其按照十六进制数解析
如果字符串以0开头,将其按照10进制解析,而不是8进制

parseInt('0x10') // 16
parseInt('011') // 11

在这里插入图片描述上图出现的情况应该是由于8进制在JS底层直接转为10进制,因此之后转为字符串并转为整数时返回相应结果

对于那些会自动转为科学计数法的数字,parseInt会将科学计数法的表示方法视为字符串,因此导致一些奇怪的结果

parseInt(1000000000000000000000.5) // 1
// 等同于
parseInt('1e+21') // 1

parseInt(0.0000008) // 8
// 等同于
parseInt('8e-7') // 8

2 用于进制转换
parseInt方法还可以接受第二个参数(2到36之间)用于表示被解析的值的进制,返回改制对应的十进制数
默认情况下,parseInt的第二个参数为10,即默认是"十"进制转十进制

parseInt('1000') // 1000
// 等同于
parseInt('1000', 10) // 1000

parseInt('1000', 2) // 8
parseInt('1000', 6) // 216
parseInt('1000', 8) // 512

上面代码中,二进制、六进制、八进制的1000,分别转换为十进制的8、216和512

如果第二个参数不是数值,会被自动转为一个整数
这个整数只有在2到36之间,才能得到有意义的结果,超出这个范围,则返回NaN
注意如果第二个参数是0、undefined和null,则直接忽略,也就是还是认为是十进制

如果字符串包含对于指定进制无意义的字符,则从最高位开始,只返回可以转换的数值
如果最高位无法转换,则直接返回NaN

parseInt('10', 37) // NaN
parseInt('10', 1) // NaN
parseInt('10', 0) // 10
parseInt('10', null) // 10
parseInt('10', undefined) // 10

parseInt('1546', 2) // 1
parseInt('546', 2) // NaN

前面说过,如果parseInt的第一个参数不是字符串,会被先转为字符串
这会导致一些令人意外的结果

parseInt(0x11, 36) // 43
parseInt(0x11, 2) // 1

// 等同于
parseInt(String(0x11), 36)
parseInt(String(0x11), 2)

// 等同于
parseInt('17', 36)
parseInt('17', 2)

上面代码中,十六进制的0x11会被先转为十进制的17,再转为字符串。然后,再用36进制或二进制解读字符串17,最后返回结果43和1,这也说明前面我们的想法是对的

这种处理方式对于八进制的前缀0尤其需要注意

parseInt(011, 2) // NaN

// 等同于
parseInt(String(011), 2)

// 等同于
parseInt(String(9), 2)

上面代码中,第一行的011会被先转为字符串9,因为9不是二进制的有效字符,所以返回NaN
如果直接计算parseInt(‘011’, 2),011则是会被当作二进制处理,返回3

JavaScript 不再允许将带有前缀0的数字视为八进制数,而是要求忽略这个0
但是为了保证兼容性,大部分浏览器并没有部署这一条规定

parseFloat()

用于将字符串转为浮点数
如果字符串符合科学计数法,则会进行相应的转换
如果字符串包含不能转为浮点数的字符,则不再进行往后转换,返回已经转好的部分

parseFloat('3.14') // 3.14

parseFloat('314e-2') // 3.14
parseFloat('0.0314E+2') // 3.14

parseFloat('3.14more non-digit characters') // 3.14

parseFloat方法会自动过滤字符串前导的空格
如果参数不是字符串,则会先转为字符串再转换
如果字符串的第一个字符不能转化为浮点数,则返回NaN

parseFloat('\t\v\r12.34\n ') // 12.34

parseFloat([1.23]) // 1.23
// 相当于
parseFloat(String([1.23])) // 1.23

parseFloat([]) // NaN
parseFloat('FF2') // NaN
parseFloat('') // NaN

上面代码中,尤其值得注意,parseFloat(同parseInt)会将空字符串转为NaN
这些特点使得他们的转换结果不同于Number函数

parseFloat(true)  // NaN
parseInt(true)  // NaN
Number(true) // 1

parseFloat(null) // NaN
parseInt(null) // NaN
Number(null) // 0

parseFloat('') // NaN
parseInt('') // NaN
Number('') // 0

parseFloat('123.45#') // 123.45
parseInt('123.45#') // 123
Number('123.45#') // NaN
isNaN()

用于判断值是否为NaN
但是,isNaN 方法只对数值有效,如果传入其他值,会被先转成数值

isNaN(NaN) // true
isNaN(123) // false

isNaN('Zane') // true
// 相当于
isNaN(Number('Zane')) // true

isNaN({}) // true
// 等同于
isNaN(Number({})) // true

isNaN(['xzy']) // true
// 等同于
isNaN(Number(['xzy'])) // true

但是对于空数组和只有一个数值成员的数组,isNaN返回false

isNaN([]) // false
inNaN([123]) // false
isNaN(['123']) // false

上面代码之所以返回false,原因是这些数组能被Number函数转成数值
在这里插入图片描述使用isNaN之前最好判断一下数据类型

function myIsNaN(value) {
	return typeof value === 'number' && isNaN(value);
}

而判断NaN更可靠的方法是利用NaN为唯一不等于自身的值的这个特点

function myIsNaN(value) {
	return value !== value;
}
isFinite()

该方法返回布尔值,表示该值是否为正常数值

isFinite(Infinity) // false
isFinite(-Infinity) // false
isFinite(NaN) // false
isFinite(undefined) // false

isFinite(null) // true
isFinite(-1) // true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zanebla

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值