数据为什么需要类型?
不同类型的数据有各自的功能
- 数字有数学含义,能够进行数学的加减乘除、取余等运算,而字符串不行。
- 字符串能够表示电话号码,而数字不行
存储的形式不同
- JS中,数字是用64位浮点数的形式存储,JS的数字只有这种存储方式,所以JS中的数字没有严格意义的整数。
- JS中,字符串的存储使用的是类似UTF-8的UCS-2存储的。
数据的类型
JS的数据总共 “四基两空一对象” 7种类型(大小写无所谓)。分别为,“四个基本类型”:number/string/boolean/symbol;“两空”:undefined/null;“一对象”:object。
注意:常用的function函数,array数组都不是数据类型,它们都属于object。
number 数字
数字的存储方式
64位浮点数
- JS中数字的存储方式只有64位浮点数,所以JS中的所有数字都是小数。
- 浮点数意思就是,小数点可以任意浮动的数。
- 64位浮点数就是我们说的双精度浮点数
双精度浮点数(64位浮点数)的转换
-
参考文档:
-
浮点数的规则
- 符号位:1位,最终计算的时候以 (-1)^符号位 来表示,所以0代表正数,1代表负数
- 阶码:11位,作为指数。2^11 = 2047,所以double的指数位的范围是0~2047,但因为在科学计数法中是可以出现负数的,所以指数位的真实值必须减去一个中间值1023(即实际上指数位的范围是-1023~1024)。如,2^10,真实的指数为10,转成2进制为1010,但是在转换成浮点数的指数位时要先+1023再转换,即 10000001001 这个就是2^10转换成浮点数的阶码。
- 尾数:52位,即有效数字。计算机保存浮点数的有效数字时,总是默认这个数第一位是1,所以舍去这个数,等到读取的时候再把1补回去,这也样实际上就能保存53位有效数字。比如,在保存1.01的时候,只保存01。
0.5的二进制浮点数表示:
0.5的二进制浮点数表示:
上面给出了0.4的二进制表示的计算方法:
0.4=0.5×0+0.25×1+0.125×1+……+0.5×(1 or 0)/n+……。
它是无穷尽的,直到精度合适了为止。然而对于有的数来说,是有穷的,比如
0.5=1×0.5。整数部分为0,小数部分为0.1,所以0.5的二进制形式是0.1,即1.0 × 2^-1。计算阶码时,用127+(-1)=126=b1111110。
所以最终结果是单精度浮点数: 0 01111110 00000000000000000000000
双精度浮点数:0 01111111110 0000...000(52个0,因为1.0省去了前面的1)
-12.5的二进制浮点表示:
整数部分为12,即b1100;小数部分为0.5,即b0.1,即1100.10000000000000000000,即1.10010000000000000000000 × 2^3。
计算阶码,3+1023=130,即b10000010
所以最终double是: 1 10000000010 10010000000000000000000000...0(有效数字共52位)
64位浮点数的范围(忽略符号位)
- 指数拉满、有效数字拉满,得到最大的二进制数字,用 Number.MAX_VALUE 即可输出,结果为: 1.7976931348623157e+308。
- 指数负方向拉满,有效数字最小1,得到最小值,用 Number.MIN_VALUE 即可输出,结果为: 5e-324。
64位浮点数有效数字的精度
- 由于有效数字实际上为53个二进制位来表示有效数字;
- 2^53对应的十进制是9后面15个0,共16位;
- 所以十进制下的15位及15位以下的有效数字都能精确表示;
- 16位有效数字如果小于90开头也能精确表示;
- 但是比如 9110000000000001就无法准确表示出来,因为有效数字会经过一些舍去的计算来保证不会超过53位的二进制;
Number的写法
- 整数
- 1、213、5487...
- 小数
- 0.1 0.224 0.5
- 科学计数法
- 1.23e4
- 八进制写法(0开头用的较少)
- 0123 0235
- 十六进制写法(0x或0X开头)
- 0x3F 0X3F
- 二进制写法(0b或0B或b开头)
- 0b11 0B11 b11
Number的一些特殊值
- +0 与 -0
- 数学含义上都是相等的,都为0。
- 但在一种情况下并不相等,就是它们当分母的时候。1/+0 = +Infinity;1/-0 = -Infinity
-
无穷大
- Infinity(正无穷大,等于+infinity) +Infinity -Infinity(负无穷大)
-
NaN
- 该值可以理解为现阶段由于知识受限无法表示的数字,也是数字类型。
- 但是由于NaN具体是什么都不知道,所以
NaN === NaN为false
字符串String
字符串的存储与编码的发展
如何存储字符串?
编号,通过将不同字符串进行编号来存储字符串。
用0~127来表示简单的符号
- 0表示结束字符
- 10表示换行
- 13表示回车
- 32表示空格
- 33~47表示标点符号
- 65~90表示大写字母
- 97~122表示小写字母
- 127表示删除键
- 三个特别的点: 48:0 ;65:A; 97:a
GB2312
由于中国和其他国家都开始使用电脑,127个字符不足以应付中国和其他国家的使用,所以中国国家标准局编写了 GB2313 编码库来扩充计算机上的符号。
其中使用0000~FFFF(16位2进制)来表示不同字符,FFFF转换成十进制即65536,所以GB2312最多可以收录65536个字符,但是这个库也一开始也就收录了6000多个汉字、字母和日文假片
GBK
由于计算机的使用范围更广了,发现有很多字符都没有收录到GB2312中,而且类似韩国这些国家的文字也没有收录在其中,GB2312不能满足用户需求了。微软在GB2312的基础上推出了GBK。
GBK收录了21886个汉字和图形符号,收录了中日韩几乎所有汉字,并且兼容GB2312。由于收录的字符数仍不足65536个,所以仍使用的仍是16位2进制数字进行存储。
GB18030
由于中国国标局推出的用于取代GBK的编码库,但由于这个编码库不兼容GB2312所以很少使用。
Unicode万国码
还是因为GBK收录的字符并不能满足广大世界群众的需求,如:GBK并没有收录藏文、泰文等。
为了一次性解决这种问题,全世界计算机使用的国家参与开发了Unicode万国码编码库。
-
Unicode的优势
- 至今收录了13万个字符(大于65536),全世界通用
- 会随着需求不断扩充,最新一版由于日本的年号更名“令和”,所以Unicode中添加了“令和”这组合字(两个字但是是一个字符)
-
Unicode的劣势
- 由于Unicode收录了13万个字符,大于65536,所以2字节已经不能完全满足Unicode的需求,每个字符都用到3个及以上字节。
- 这样每个字符的大小相比GBK都是原先的1.5倍,非常浪费资源。为了解决这个问题UTF-8应运而生。
UTF-8
UTF-8并不是一个编码库,而是Unicode的一种取巧的编码方式。UTF-8中的8意思是最少可以只用8位2进制数字存储一个字符。
UTF-8的规则,以"你a"为例:
-
"a"对应的Unicode编号为97,十六进制为61
- Unicode直接编码为:00000000 00000000 01100001
- UTF-8编码为:01100001
-
"你"对应的Unicode编号为0x4F60
- Unicode直接编码为:00000000 01001111 01100000
- UTF-8编码为:11100100 10111101 10100000
-
根据上面的信息"你a"UTF-8存储为:11100100101111011010000001100001,那么如何读取呢?
- 先读前8位信息,11100100,这开头的3个1,说明这个字符有3个8位
- 于是再读2个8位,11100100 10111101 10100000
- 第1个8位,除去编码字符数的信息:1110;后面2个8位除去开头的10
- 结合为 0100 111101 100000 合并为8位一字节的形式, 01001111 01100000,这就是Unicode直接编码的"你"
- 再读8位 01100001,开头是0,说明这个字符只占有8位,即UTF-8编码等于Unicode直接编码
- 即 00000000 00000000 01100001 对应为"a"
- 到此 "你a"读取完毕。
字符串的语法
- 单引号:'你好'
- 双引号:"你好"
- 反引号:`你好` 反引号声明字符串是ES6的新特性,它有更多的功能
- 注意:引号并不是字符串中的一部分
转义
字符串中有单引号、双引号、反引号怎么办?
-
当字符串中只有3种引号中的1种或两种,我们使用字符串中不包含的引号来进行创建
"it's me"用双引号包裹这个单引号- 但是这种方法在不同的情况下可能有不一样的效果
-
最好的方法使用转义
- 转义就是用另一种写法,表示想要的内容
- \' 表示 '
- \" 表示"
- \n 表示换行
- \r 表示回车
- \t 表示tab制表符
- \ 表示 \
- \uFFFF 表示FFFF对应的Unicode字符
- \xFF 表示前256个Unicode字符
多行字符串
单引号、双引号声明字符串都不能实现这个效果,会因为引号不闭合而报错。为了解决多行字符串的问题,ES6推出了使用反引号。
字符串的属性
- 字符串的长度
[string].length
'123'.length //3
'\n\r\t'.length //3
'\\\\\\'.length //3
''.length //0
' '.length //1(字符串里1个空格)
' '.length //3(字符串里3个空格) 字符串中每个空格都算一个字符
2.通过下标读取字符
[string].string[index]
let s = '123456'
s.string[0] // 1 注意:index是从0开始的
s.string[5] //6
base64转码
window.btoa:将正常字符串转码为base64编码的字符串window.atob:将base64编码的字符串转码为正常字符串- 作用:用来隐藏一些简单的信息,如邮箱等。用来加密,不过太简单,就是在骗自己。
布尔值 boolean
- 布尔值只有 true 和 false
运用到布尔值的运算
- 否定运算 !value
- 相等运算运算 == != === !==
- 比较运算 > >= < <=
- 布尔值通常用于 if语句 中的判断
falsy与false
falsy就是作用相当于false,但是又不是false的值
- undefined
- null
- 0
- NaN
- '' (空字符串,中间没有空格)
undefinded与null
- 两者没有本质区别,都表示空
- 如果一个变量声明了但是没有被赋值,那么默认值就是undefined
- 如果一个函数没有return任何东西,JS会默认这个函数 return undefined
- 开发者通常习惯把非对象的空值写成undefined,对象的空值写为null,但这也仅仅是习惯。
Symbol
简单来说:Symbol就是生成一个全局唯一的值。
一些数据类型的转换
number => string
let a = 123
// 1. Stirng()方法
String(a) // '123'
// BUG:当转换的数字太大,转换成的字符串会出现一个科学技术法的形式
String(100000000000000000000000) // "1e+23"
// 2. 巧用字符串的拼接,字符串和任何类型的数据拼接最终的类型都是字符串
a + '' // '123'
string => number
let a = '123'
let b = 'hello123'
// 1. Number()方法
Number(a) // 123,字符串中只有数字时,才能转换成对应数字的Number
Number(b) // NaN,字符串中不只有数字时,转换的Number是NaN
// 2. parseInt() 与 parseFloat() 方法
parseInt('123.456') // 123 四舍五入转换成整数
parseFloat('123.456') // 123.456 直接转换成浮点数
/* parseInt() 第二个参数是设置按照多少进制来转换
parseInt('0123') 旧JS看到0开头的就以为是8进制,所以需要手动给第二个参数10 */
// 3.减法运算 或 +号 (通过Number特有的运算功能)
'123.456' - 0 // 123.456
+'123.456' // 123.456
任何类型转 Boolean
// 转布尔值时除了那几个 falsy值 为负之外,其他都为正
// 1. Boolean()
Boolean(1) // true
Boolean(0) // false
Boolean('1') // true
Boolean('') // false
Boolean(undefiend) // false
Boolean(null) // false
Boolean(NaN) // false
// 2. !! 一个"!"就是取非(布尔值)两个"!"就是取非再取非,就实现了布尔值的转换
!!0 // false
!!1 // true
任何类型转 String
// 1. [变量].toString()
true.toString() // 'true'
1.toString() // 报错! 这是JS的一个BUG。当数字类型使用这个方法转字符串时,JS会认为 . 后面的toString是这个数的一部分。
// 妥协用法
(1).toString() // 1
1..toString() // 1
// 2.String(变量)
String(true) // "true"