概述
JavaScript的数据类型共有8种(大小写无所谓):
- 数值(number)
- 字符串(string)
- 布尔值(boolean)
- undefined
- null
- 对象(object,并且object叫做复杂类型,上面六种叫简单类型)
- Symbol(很少用,可以看方方老师写的博客)
- BigInt(2020年出的数据类型,现在用得很少,先了解一下)
注意以下不是数据类型:
- 数组、函数、日期
- 它们都属于object
编程语言为什么需要类型
需要分清楚 数字1
和 字符串1
?即 1 和 "1"
!
- 功能上不同
- 数字是数字,字符串是字符串,必须严谨。
- 数字可以加减乘除,字符串不可以。
- 字符串可以表示电话号码,数字不行。(注意,电话号码可不单只有数字,有些国家的电话号码有英文字母与数字组合或者纯字母表示,所以只能是字符串。)
- 储存形式不同(还记得JS的内存图吗?)
- 在JS中,数字用64位浮点数的形式存储。
- 字符串用类似UTF8形式存储的(UCS-2),总之存字符串就是编号,然后存编号。
typeof 运算符
如何判断 值
是什么类型的数值?
直接用 typeof
后面接该数值就可以了,如
typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"
数值(number)
写法
- 整数:1
- 小数:0.1
- 科学计数法:1.23e4
- 八进制(比较少用):0123 or 00123 or 0o123
- 十六进制写法:0x3F or 0X3F
- 二进制写法:0b11 or 0B11
还可以进行类型转换,number => string
String(n)
n + ''
特殊值
正零和负零
//0分正零(+1)和负零(-1),但是这两个零都等于0,大部分情况下是一样的:
-0 === +0 //true
0 === -0 //true
0 === +0 //true
//除了这情况:
1/+0 等于 +Infinity
1/-0 等于 -Infinity
-Infinity === +Infinity //false
整数和浮点数
JavaScript其实是没有整数的,因为在JS中,所有数字都是以64位浮点数形式存储,也就是说,1和1.0其实是一样的。
1 === 1.0 //true
注意,浮点数不是精确的值,所有涉及到的浮点数运算和比较都需要注意,如
0.1 + 0.2 === 0.3 //false
0.3 / 0.1 // 2.9999999999999996
如果想要正确的比较方法,需要如下:
console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON ) //true
NaN(不是一个数)
表示“非数字”(Not a Number),实验可以得出 NaN
的有:
0/0 //NaN
更神奇的是, NaN
虽然表示不是一个数,但 NaN
确实属于数值:
typeof NaN // NaN
在运算方面,任何跟 NaN
运算的数值,都等于 NaN
,并且 NaN
不等于 NaN
,想想都知道:
NaN + 32 // NaN
NaN - 32 // NaN
NaN * 32 // NaN
NaN / 32 // NaN
0 * Infinity // NaN
Infinity - Infinity // NaN
Infinity / Infinity // NaN
//并且 NaN 不等于 NaN
NaN === NaN // false
数值范围
根据标准,64位浮点数的指数部分的长度是11个二进制位,意味着指数部分的最大值是2047(2的11次方减1)。 也就是说,64位浮点数的指数部分的值最大为2047,分出一半表示负数,则 JavaScript 能够表示的数值范围为21024到2-1023(开区间),超出这个范围的数无法表示(即+Infinity或-Infinity)。
—-网道JavaScript
Number.MAX_VALUE // 1.7976931348623157e+308 表示最大值是多少
Number.MIN_VALUE // 5e-324 表示最小值是多少
字符串(string)
类型转换:string => number
Number(s)
parseInt(s)/parseFloat(s)
s - 0
其他类型转字符串:x => string
String(x)
x.toString()
写法
- 单引号 ‘你好’
- 双引号 “你好”
- 反引号
你好
注意:引号不属于字符串的一部分,就好像书名号不属于书名一样。
转义
可以用单引号括双引号,双引号括单引号,但是不可以括相同的引号:
"我想说:'你好!'" //合法
'我想说:"你好!"' //合法
`我想说:'你好!'` //合法
`我想说:"你好!"` //合法
"我想说:"你好!"" //错误的写法,单引号同理
可我就是要一样怎么办?
可以选择用转义的方法:
"我想说:\"你好!\"" //在想要包括在括号里面的字符的前面加一个反斜杠即可。
'it\'s OK'
转义有很多用途,如
//字符串默认只能写一行,不可以多行,以下是属于不合法的字符串:
'a
b
c'
//但我就是想要多行怎么办?可以用转义的方式:
'a\
b\
c'//或者用反引号
`a
b
c`
//'a b c'写代码时虽然是多行,但输出的时候还是一行。
除了这个方式,还有很多特殊用途:
\0
:null(\u0000
)\n
:换行符(\u000A
)\r
:回车键(\u000D
)\t
:制表符(\u0009
)\'
:单引号(\u0027
)\"
:双引号(\u0022
)\\
:反斜杠(\u005C
)\uFFFF
表示对应的Unicode字符\xFF
表示前256个Unicode字符
字符串与数组
字符串可以被视为字符数组,因此可以使用数组的方括号运算符,用来返回某个位置的字符(位置编号从0开始)。
var s = 'hello';
s[0] // "h"
s[1] // "e"
s[4] // "o"
// 直接对字符串使用方括号运算符
'hello'[1] // "e"
而且不可以单个字符更改或删除,浏览器不会报错但默默的失败
var s = 'hello';
delete s[0];
s // "hello"
s[1] = 'a';
s // "hello"
【问】字符串有最大长度吗?
【答】有,长度为2^53 - 1
,但这个长度不是字符数!
实际上字符是以Unicode的方式表示,而一个Unicode的码点表示一个字符,常见的编码有 UTF8 和 UTF16 。在JS中,字符串并不是意义上的 String
,而是编码 UTF16,字符串的操作 charAt、charCodeAt、length等方法都是针对UTF16编码。所以说,字符串长度取决于编码长度。
布尔(boolean)
只有 true
和 false
两个值,注意大小写。
通过比较、运算就可以得出布尔值。通常需要配合判断真假语句 if(value){...}else{...}
。
问题来了,value如果不是比较和运算得出来的bool值怎么办?这时候就可以通过该value是否是falsy值了。
falsy值是什么东西?
falsy就相当于false但又不是false的值,分别是:
undefined
null
false
0
NaN
""
或''
(空字符串,里面如果有空格就不是空字符串了," "
和' '
)
除了falsy值,其他的均为true!
类型转换:x => bool
Boolean(x)
!!x
undefined 和 null
【问】这两种都代表空类型,为什么有两个空?
【答】undefined表示未定义,它的值只有undefined
。而null表示已定义但是为空,值同样只有一个null
。
一般情况下:
- 这两个类型没有本质区别。
- 任何一个声明变量后,在没赋值前,值都为undefined。
- 一个函数,如果没有写return,那么默认return undefined,而不是null。
- 前端习惯上,把非对象的空值写为undefined,把对象的空值写为null。当然这个习惯可以打破。
【问】为什么有的编程规范要求用void 0
代替undefined
?
【补充】void运算可以把任意一个表达式变成undefined,void 0
与undefined
是等价的,即使这两个相互判断也为true。
【答】因为undefined
不是关键字(null
是关键字),而是一个变量(设计失误),为了避免被篡改,而选择使用void 0
。
因为变量在没赋值之前的值为undefined
,所以在编写代码的时候,不会赋值undefined
而是赋值null
,这样就保证在整个项目里的nudefined不是赋值的。
Symbol
作用是可以生成全局唯一的值。不支持new方法。
在规范里,对象的属性键除了是字符串外,还可以是Symbol,只有这两种类型。
具体创建及使用如下:
let id = Symbol();
或者添加描述:
let id = Symbol('id')
//里面的描述“id”相当与是注释,与Symbol本身作用没什么关系,但方便调试。
保证全局唯一,即使里面的描述是一样的,Symbol的值也是不一样的。例如:
let id1 = Symbol("id")
let id2 = Symbol("id")
console.log(id1 === id2) //false
使用场景1:“隐藏”对象属性
如下:
let id = Symbol("id")
let user = {
name: "John",
[id]: 123 // 而不是 "id":123
};
对象里面的[id]
,不会被for...in..
遍历到。并且也不会被别的脚本直接访问到,因为另一个脚本没有这个脚本的Symbol。因此,该属性将受到保护,防止被意外修改或重写。
JSON.stringify
也会直接忽略Symbol。
但是,也不是100%隐藏,可以使用 Object.getOwnPropertySymbols(obj)
,允许获取所有的Symbol。还有个可以返回一个对象的所有键Reflect.ownKeys(obj)
。
使用场景2:系统Symbol
JS有许多系统Symbol,可以使用Symbol.*
来访问使用。例如:
- Symbol.iterator
- Symbol.toPrimitive
- 等。。。